/** @file

Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>

SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <PiPei.h>
#include <PiDxe.h>
#include <PiSmm.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Library/PeiServicesLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/LockBoxLib.h>
#include <Library/HobLib.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Protocol/SmmCommunication.h>
#include <Ppi/SmmCommunication.h>
#include <Ppi/SmmAccess.h>
#include <Guid/AcpiS3Context.h>
#include <Guid/SmmLockBox.h>

#include "SmmLockBoxLibPrivate.h"

#if defined (MDE_CPU_IA32)
typedef struct _LIST_ENTRY64 LIST_ENTRY64;
struct _LIST_ENTRY64 {
  LIST_ENTRY64  *ForwardLink;
  UINT32        Reserved1;
  LIST_ENTRY64  *BackLink;
  UINT32        Reserved2;
};

typedef struct {
  EFI_TABLE_HEADER    Hdr;
  UINT64              SmmFirmwareVendor;
  UINT64              SmmFirmwareRevision;
  UINT64              SmmInstallConfigurationTable;
  UINT64              SmmIoMemRead;
  UINT64              SmmIoMemWrite;
  UINT64              SmmIoIoRead;
  UINT64              SmmIoIoWrite;
  UINT64              SmmAllocatePool;
  UINT64              SmmFreePool;
  UINT64              SmmAllocatePages;
  UINT64              SmmFreePages;
  UINT64              SmmStartupThisAp;
  UINT64              CurrentlyExecutingCpu;
  UINT64              NumberOfCpus;
  UINT64              CpuSaveStateSize;
  UINT64              CpuSaveState;
  UINT64              NumberOfTableEntries;
  UINT64              SmmConfigurationTable;
} EFI_SMM_SYSTEM_TABLE2_64;

typedef struct {
  EFI_GUID                          VendorGuid;
  UINT64                            VendorTable;
} EFI_CONFIGURATION_TABLE64;
#endif

#if defined (MDE_CPU_X64)
typedef LIST_ENTRY LIST_ENTRY64;
typedef EFI_SMM_SYSTEM_TABLE2 EFI_SMM_SYSTEM_TABLE2_64;
typedef EFI_CONFIGURATION_TABLE EFI_CONFIGURATION_TABLE64;
#endif

/**
  This function return first node of LinkList queue.

  @param LockBoxQueue  LinkList queue

  @return first node of LinkList queue
**/
LIST_ENTRY *
InternalInitLinkDxe (
  IN LIST_ENTRY *LinkList
  )
{
  if ((sizeof(UINTN) == sizeof(UINT32)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) ) {
    //
    // 32 PEI + 64 DXE
    //
    return (LIST_ENTRY *)(((LIST_ENTRY64 *)LinkList)->ForwardLink);
  } else {
    return LinkList->ForwardLink;
  }
}

/**
  This function return next node of LinkList.

  @param Link  LinkList node

  @return next node of LinkList
**/
LIST_ENTRY *
InternalNextLinkDxe (
  IN LIST_ENTRY *Link
  )
{
  if ((sizeof(UINTN) == sizeof(UINT32)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) ) {
    //
    // 32 PEI + 64 DXE
    //
    return (LIST_ENTRY *)(((LIST_ENTRY64 *)Link)->ForwardLink);
  } else {
    return Link->ForwardLink;
  }
}

/**
  This function find LockBox by GUID from SMRAM.

  @param LockBoxQueue The LockBox queue in SMRAM
  @param Guid         The guid to indentify the LockBox

  @return LockBoxData
**/
SMM_LOCK_BOX_DATA *
InternalFindLockBoxByGuidFromSmram (
  IN LIST_ENTRY *LockBoxQueue,
  IN EFI_GUID   *Guid
  )
{
  LIST_ENTRY                    *Link;
  SMM_LOCK_BOX_DATA             *LockBox;

  for (Link = InternalInitLinkDxe (LockBoxQueue);
       Link != LockBoxQueue;
       Link = InternalNextLinkDxe (Link)) {
    LockBox = BASE_CR (
                Link,
                SMM_LOCK_BOX_DATA,
                Link
                );
    if (CompareGuid (&LockBox->Guid, Guid)) {
      return LockBox;
    }
  }
  return NULL;
}

/**
  Get VendorTable by VendorGuid in Smst.

  @param Signature  Signature of SMM_S3_RESUME_STATE
  @param Smst       SMM system table
  @param VendorGuid vendor guid

  @return vendor table.
**/
VOID *
InternalSmstGetVendorTableByGuid (
  IN UINT64                                        Signature,
  IN EFI_SMM_SYSTEM_TABLE2                         *Smst,
  IN EFI_GUID                                      *VendorGuid
  )
{
  EFI_CONFIGURATION_TABLE                       *SmmConfigurationTable;
  UINTN                                         NumberOfTableEntries;
  UINTN                                         Index;
  EFI_SMM_SYSTEM_TABLE2_64                      *Smst64;
  EFI_CONFIGURATION_TABLE64                     *SmmConfigurationTable64;

  if ((sizeof(UINTN) == sizeof(UINT32)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {
    //
    // 32 PEI + 64 DXE
    //
    Smst64 = (EFI_SMM_SYSTEM_TABLE2_64 *)Smst;
    SmmConfigurationTable64 = (EFI_CONFIGURATION_TABLE64 *)(UINTN)Smst64->SmmConfigurationTable;
    NumberOfTableEntries = (UINTN)Smst64->NumberOfTableEntries;
    for (Index = 0; Index < NumberOfTableEntries; Index++) {
      if (CompareGuid (&SmmConfigurationTable64[Index].VendorGuid, VendorGuid)) {
        return (VOID *)(UINTN)SmmConfigurationTable64[Index].VendorTable;
      }
    }
    return NULL;
  } else {
    SmmConfigurationTable = Smst->SmmConfigurationTable;
    NumberOfTableEntries = Smst->NumberOfTableEntries;
    for (Index = 0; Index < NumberOfTableEntries; Index++) {
      if (CompareGuid (&SmmConfigurationTable[Index].VendorGuid, VendorGuid)) {
        return (VOID *)SmmConfigurationTable[Index].VendorTable;
      }
    }
    return NULL;
  }
}

/**
  Get SMM LockBox context.

  @return SMM LockBox context.
**/
SMM_LOCK_BOX_CONTEXT *
InternalGetSmmLockBoxContext (
  VOID
  )
{
  EFI_SMRAM_DESCRIPTOR                          *SmramDescriptor;
  SMM_S3_RESUME_STATE                           *SmmS3ResumeState;
  VOID                                          *GuidHob;
  SMM_LOCK_BOX_CONTEXT                          *SmmLockBoxContext;

  GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid);
  ASSERT (GuidHob != NULL);
  SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob);
  SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart;

  SmmLockBoxContext = (SMM_LOCK_BOX_CONTEXT *)InternalSmstGetVendorTableByGuid (
                                                SmmS3ResumeState->Signature,
                                                (EFI_SMM_SYSTEM_TABLE2 *)(UINTN)SmmS3ResumeState->Smst,
                                                &gEfiSmmLockBoxCommunicationGuid
                                                );
  ASSERT (SmmLockBoxContext != NULL);

  return SmmLockBoxContext;
}

/**
  This function will restore confidential information from lockbox in SMRAM directly.

  @param Guid   the guid to identify the confidential information
  @param Buffer the address of the restored confidential information
                NULL means restored to original address, Length MUST be NULL at same time.
  @param Length the length of the restored confidential information

  @retval RETURN_SUCCESS            the information is restored successfully.
  @retval RETURN_WRITE_PROTECTED    Buffer and Length are NULL, but the LockBox has no
                                    LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE attribute.
  @retval RETURN_BUFFER_TOO_SMALL   the Length is too small to hold the confidential information.
  @retval RETURN_NOT_FOUND          the requested GUID not found.
**/
EFI_STATUS
InternalRestoreLockBoxFromSmram (
  IN  GUID                        *Guid,
  IN  VOID                        *Buffer, OPTIONAL
  IN  OUT UINTN                   *Length  OPTIONAL
  )
{
  PEI_SMM_ACCESS_PPI             *SmmAccess;
  UINTN                          Index;
  EFI_STATUS                     Status;
  SMM_LOCK_BOX_CONTEXT           *SmmLockBoxContext;
  LIST_ENTRY                     *LockBoxQueue;
  SMM_LOCK_BOX_DATA              *LockBox;
  VOID                           *RestoreBuffer;

  //
  // Get needed resource
  //
  Status = PeiServicesLocatePpi (
             &gPeiSmmAccessPpiGuid,
             0,
             NULL,
             (VOID **)&SmmAccess
             );
  if (!EFI_ERROR (Status)) {
    for (Index = 0; !EFI_ERROR (Status); Index++) {
      Status = SmmAccess->Open ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index);
    }
  }

  //
  // Get LockBox context
  //
  SmmLockBoxContext = InternalGetSmmLockBoxContext ();
  LockBoxQueue = (LIST_ENTRY *)(UINTN)SmmLockBoxContext->LockBoxDataAddress;

  //
  // We do NOT check Buffer address in SMRAM, because if SMRAM not locked, we trust the caller.
  //

  //
  // Restore this, Buffer and Length MUST be both NULL or both non-NULL
  //

  //
  // Find LockBox
  //
  LockBox = InternalFindLockBoxByGuidFromSmram (LockBoxQueue, Guid);
  if (LockBox == NULL) {
    //
    // Not found
    //
    return EFI_NOT_FOUND;
  }

  //
  // Set RestoreBuffer
  //
  if (Buffer != NULL) {
    //
    // restore to new buffer
    //
    RestoreBuffer = Buffer;
  } else {
    //
    // restore to original buffer
    //
    if ((LockBox->Attributes & LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE) == 0) {
      return EFI_WRITE_PROTECTED;
    }
    RestoreBuffer = (VOID *)(UINTN)LockBox->Buffer;
  }

  //
  // Set RestoreLength
  //
  if (Length != NULL) {
    if (*Length < (UINTN)LockBox->Length) {
      //
      // Input buffer is too small to hold all data.
      //
      *Length = (UINTN)LockBox->Length;
      return EFI_BUFFER_TOO_SMALL;
    }
    *Length = (UINTN)LockBox->Length;
  }

  //
  // Restore data
  //
  CopyMem (RestoreBuffer, (VOID *)(UINTN)LockBox->SmramBuffer, (UINTN)LockBox->Length);

  //
  // Done
  //
  return EFI_SUCCESS;
}

/**
  This function will restore confidential information from all lockbox which have RestoreInPlace attribute.

  @retval RETURN_SUCCESS            the information is restored successfully.
**/
EFI_STATUS
InternalRestoreAllLockBoxInPlaceFromSmram (
  VOID
  )
{
  PEI_SMM_ACCESS_PPI             *SmmAccess;
  UINTN                          Index;
  EFI_STATUS                     Status;
  SMM_LOCK_BOX_CONTEXT           *SmmLockBoxContext;
  LIST_ENTRY                     *LockBoxQueue;
  SMM_LOCK_BOX_DATA              *LockBox;
  LIST_ENTRY                     *Link;

  //
  // Get needed resource
  //
  Status = PeiServicesLocatePpi (
             &gPeiSmmAccessPpiGuid,
             0,
             NULL,
             (VOID **)&SmmAccess
             );
  if (!EFI_ERROR (Status)) {
    for (Index = 0; !EFI_ERROR (Status); Index++) {
      Status = SmmAccess->Open ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index);
    }
  }

  //
  // Get LockBox context
  //
  SmmLockBoxContext = InternalGetSmmLockBoxContext ();
  LockBoxQueue = (LIST_ENTRY *)(UINTN)SmmLockBoxContext->LockBoxDataAddress;

  //
  // We do NOT check Buffer address in SMRAM, because if SMRAM not locked, we trust the caller.
  //

  //
  // Restore all, Buffer and Length MUST be NULL
  //
  for (Link = InternalInitLinkDxe (LockBoxQueue);
       Link != LockBoxQueue;
       Link = InternalNextLinkDxe (Link)) {
    LockBox = BASE_CR (
                Link,
                SMM_LOCK_BOX_DATA,
                Link
                );
    if ((LockBox->Attributes & LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE) != 0) {
      //
      // Restore data
      //
      CopyMem ((VOID *)(UINTN)LockBox->Buffer, (VOID *)(UINTN)LockBox->SmramBuffer, (UINTN)LockBox->Length);
    }
  }
  //
  // Done
  //
  return EFI_SUCCESS;
}

/**
  This function will save confidential information to lockbox.

  @param Guid       the guid to identify the confidential information
  @param Buffer     the address of the confidential information
  @param Length     the length of the confidential information

  @retval RETURN_SUCCESS            the information is saved successfully.
  @retval RETURN_INVALID_PARAMETER  the Guid is NULL, or Buffer is NULL, or Length is 0
  @retval RETURN_ALREADY_STARTED    the requested GUID already exist.
  @retval RETURN_OUT_OF_RESOURCES   no enough resource to save the information.
  @retval RETURN_ACCESS_DENIED      it is too late to invoke this interface
  @retval RETURN_NOT_STARTED        it is too early to invoke this interface
  @retval RETURN_UNSUPPORTED        the service is not supported by implementaion.
**/
RETURN_STATUS
EFIAPI
SaveLockBox (
  IN  GUID                        *Guid,
  IN  VOID                        *Buffer,
  IN  UINTN                       Length
  )
{
  ASSERT (FALSE);

  //
  // No support to save at PEI phase
  //
  return RETURN_UNSUPPORTED;
}

/**
  This function will set lockbox attributes.

  @param Guid       the guid to identify the confidential information
  @param Attributes the attributes of the lockbox

  @retval RETURN_SUCCESS            the information is saved successfully.
  @retval RETURN_INVALID_PARAMETER  attributes is invalid.
  @retval RETURN_NOT_FOUND          the requested GUID not found.
  @retval RETURN_ACCESS_DENIED      it is too late to invoke this interface
  @retval RETURN_NOT_STARTED        it is too early to invoke this interface
  @retval RETURN_UNSUPPORTED        the service is not supported by implementaion.
**/
RETURN_STATUS
EFIAPI
SetLockBoxAttributes (
  IN  GUID                        *Guid,
  IN  UINT64                      Attributes
  )
{
  ASSERT (FALSE);

  //
  // No support to save at PEI phase
  //
  return RETURN_UNSUPPORTED;
}

/**
  This function will update confidential information to lockbox.

  @param Guid   the guid to identify the original confidential information
  @param Offset the offset of the original confidential information
  @param Buffer the address of the updated confidential information
  @param Length the length of the updated confidential information

  @retval RETURN_SUCCESS            the information is saved successfully.
  @retval RETURN_INVALID_PARAMETER  the Guid is NULL, or Buffer is NULL, or Length is 0.
  @retval RETURN_NOT_FOUND          the requested GUID not found.
  @retval RETURN_BUFFER_TOO_SMALL   for lockbox without attribute LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY,
                                    the original buffer to too small to hold new information.
  @retval RETURN_OUT_OF_RESOURCES   for lockbox with attribute LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY,
                                    no enough resource to save the information.
  @retval RETURN_ACCESS_DENIED      it is too late to invoke this interface
  @retval RETURN_NOT_STARTED        it is too early to invoke this interface
  @retval RETURN_UNSUPPORTED        the service is not supported by implementaion.
**/
RETURN_STATUS
EFIAPI
UpdateLockBox (
  IN  GUID                        *Guid,
  IN  UINTN                       Offset,
  IN  VOID                        *Buffer,
  IN  UINTN                       Length
  )
{
  ASSERT (FALSE);

  //
  // No support to update at PEI phase
  //
  return RETURN_UNSUPPORTED;
}

/**
  This function will restore confidential information from lockbox.

  @param Guid   the guid to identify the confidential information
  @param Buffer the address of the restored confidential information
                NULL means restored to original address, Length MUST be NULL at same time.
  @param Length the length of the restored confidential information

  @retval RETURN_SUCCESS            the information is restored successfully.
  @retval RETURN_INVALID_PARAMETER  the Guid is NULL, or one of Buffer and Length is NULL.
  @retval RETURN_WRITE_PROTECTED    Buffer and Length are NULL, but the LockBox has no
                                    LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE attribute.
  @retval RETURN_BUFFER_TOO_SMALL   the Length is too small to hold the confidential information.
  @retval RETURN_NOT_FOUND          the requested GUID not found.
  @retval RETURN_NOT_STARTED        it is too early to invoke this interface
  @retval RETURN_ACCESS_DENIED      not allow to restore to the address
  @retval RETURN_UNSUPPORTED        the service is not supported by implementaion.
**/
RETURN_STATUS
EFIAPI
RestoreLockBox (
  IN  GUID                        *Guid,
  IN  VOID                        *Buffer, OPTIONAL
  IN  OUT UINTN                   *Length  OPTIONAL
  )
{
  EFI_STATUS                         Status;
  EFI_PEI_SMM_COMMUNICATION_PPI      *SmmCommunicationPpi;
  EFI_SMM_LOCK_BOX_PARAMETER_RESTORE *LockBoxParameterRestore;
  EFI_SMM_COMMUNICATE_HEADER         *CommHeader;
  UINT8                              CommBuffer[sizeof(EFI_GUID) + sizeof(UINT64) + sizeof(EFI_SMM_LOCK_BOX_PARAMETER_RESTORE)];
  UINTN                              CommSize;
  UINT64                             MessageLength;

  //
  // Please aware that there is UINTN in EFI_SMM_COMMUNICATE_HEADER. It might be UINT64 in DXE, while it is UINT32 in PEI.
  // typedef struct {
  //   EFI_GUID  HeaderGuid;
  //   UINTN     MessageLength;
  //   UINT8     Data[1];
  // } EFI_SMM_COMMUNICATE_HEADER;
  //

  DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib RestoreLockBox - Enter\n"));

  //
  // Basic check
  //
  if ((Guid == NULL) ||
      ((Buffer == NULL) && (Length != NULL)) ||
      ((Buffer != NULL) && (Length == NULL))) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Get needed resource
  //
  Status = PeiServicesLocatePpi (
             &gEfiPeiSmmCommunicationPpiGuid,
             0,
             NULL,
             (VOID **)&SmmCommunicationPpi
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib LocatePpi - (%r)\n", Status));
    Status = InternalRestoreLockBoxFromSmram (Guid, Buffer, Length);
    DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib RestoreLockBox - Exit (%r)\n", Status));
    return Status;
  }

  //
  // Prepare parameter
  //
  CommHeader = (EFI_SMM_COMMUNICATE_HEADER *)&CommBuffer[0];
  CopyMem (&CommHeader->HeaderGuid, &gEfiSmmLockBoxCommunicationGuid, sizeof(gEfiSmmLockBoxCommunicationGuid));
  if ((sizeof(UINTN) == sizeof(UINT32)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) ) {
    MessageLength = sizeof(*LockBoxParameterRestore);
    CopyMem (&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, MessageLength)], &MessageLength, sizeof(MessageLength));
  } else {
    CommHeader->MessageLength = sizeof(*LockBoxParameterRestore);
  }

  DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib CommBuffer - %x\n", &CommBuffer[0]));
  if ((sizeof(UINTN) == sizeof(UINT32)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) ) {
    LockBoxParameterRestore = (EFI_SMM_LOCK_BOX_PARAMETER_RESTORE *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, MessageLength) + sizeof(UINT64)];
  } else {
    LockBoxParameterRestore = (EFI_SMM_LOCK_BOX_PARAMETER_RESTORE *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, MessageLength) + sizeof(UINTN)];
  }
  DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib LockBoxParameterRestore - %x\n", LockBoxParameterRestore));
  LockBoxParameterRestore->Header.Command    = EFI_SMM_LOCK_BOX_COMMAND_RESTORE;
  LockBoxParameterRestore->Header.DataLength = sizeof(*LockBoxParameterRestore);
  LockBoxParameterRestore->Header.ReturnStatus = (UINT64)-1;
  if (Guid != 0) {
    CopyMem (&LockBoxParameterRestore->Guid, Guid, sizeof(*Guid));
  } else {
    ZeroMem (&LockBoxParameterRestore->Guid, sizeof(*Guid));
  }
  LockBoxParameterRestore->Buffer = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
  if (Length != NULL) {
    LockBoxParameterRestore->Length = (EFI_PHYSICAL_ADDRESS)*Length;
  } else {
    LockBoxParameterRestore->Length = 0;
  }

  //
  // Send command
  //
  CommSize = sizeof(CommBuffer);
  Status = SmmCommunicationPpi->Communicate (
                                  SmmCommunicationPpi,
                                  &CommBuffer[0],
                                  &CommSize
                                  );
  if (Status == EFI_NOT_STARTED) {
    //
    // Pei SMM communication not ready yet, so we access SMRAM directly
    //
    DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib Communicate - (%r)\n", Status));
    Status = InternalRestoreLockBoxFromSmram (Guid, Buffer, Length);
    LockBoxParameterRestore->Header.ReturnStatus = (UINT64)Status;
    if (Length != NULL) {
      LockBoxParameterRestore->Length = (UINT64)*Length;
    }
  }

  if (Length != NULL) {
    *Length = (UINTN)LockBoxParameterRestore->Length;
  }

  Status = (EFI_STATUS)LockBoxParameterRestore->Header.ReturnStatus;
  if (Status != EFI_SUCCESS) {
    // Need or MAX_BIT, because there might be case that SMM is X64 while PEI is IA32.
    Status |= MAX_BIT;
  }

  DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib RestoreLockBox - Exit (%r)\n", Status));

  //
  // Done
  //
  return Status;
}

/**
  This function will restore confidential information from all lockbox which have RestoreInPlace attribute.

  @retval RETURN_SUCCESS            the information is restored successfully.
  @retval RETURN_NOT_STARTED        it is too early to invoke this interface
  @retval RETURN_UNSUPPORTED        the service is not supported by implementaion.
**/
RETURN_STATUS
EFIAPI
RestoreAllLockBoxInPlace (
  VOID
  )
{
  EFI_STATUS                                      Status;
  EFI_PEI_SMM_COMMUNICATION_PPI                   *SmmCommunicationPpi;
  EFI_SMM_LOCK_BOX_PARAMETER_RESTORE_ALL_IN_PLACE *LockBoxParameterRestoreAllInPlace;
  EFI_SMM_COMMUNICATE_HEADER                      *CommHeader;
  UINT8                                           CommBuffer[sizeof(EFI_GUID) + sizeof(UINT64) + sizeof(EFI_SMM_LOCK_BOX_PARAMETER_RESTORE_ALL_IN_PLACE)];
  UINTN                                           CommSize;
  UINT64                                          MessageLength;

  //
  // Please aware that there is UINTN in EFI_SMM_COMMUNICATE_HEADER. It might be UINT64 in DXE, while it is UINT32 in PEI.
  // typedef struct {
  //   EFI_GUID  HeaderGuid;
  //   UINTN     MessageLength;
  //   UINT8     Data[1];
  // } EFI_SMM_COMMUNICATE_HEADER;
  //

  DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib RestoreAllLockBoxInPlace - Enter\n"));

  //
  // Get needed resource
  //
  Status = PeiServicesLocatePpi (
             &gEfiPeiSmmCommunicationPpiGuid,
             0,
             NULL,
             (VOID **)&SmmCommunicationPpi
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib LocatePpi - (%r)\n", Status));
    Status = InternalRestoreAllLockBoxInPlaceFromSmram ();
    DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib RestoreAllLockBoxInPlace - Exit (%r)\n", Status));
    return Status;
  }

  //
  // Prepare parameter
  //
  CommHeader = (EFI_SMM_COMMUNICATE_HEADER *)&CommBuffer[0];
  CopyMem (&CommHeader->HeaderGuid, &gEfiSmmLockBoxCommunicationGuid, sizeof(gEfiSmmLockBoxCommunicationGuid));
  if ((sizeof(UINTN) == sizeof(UINT32)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) ) {
    MessageLength = sizeof(*LockBoxParameterRestoreAllInPlace);
    CopyMem (&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, MessageLength)], &MessageLength, sizeof(MessageLength));
  } else {
    CommHeader->MessageLength = sizeof(*LockBoxParameterRestoreAllInPlace);
  }

  if ((sizeof(UINTN) == sizeof(UINT32)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) ) {
    LockBoxParameterRestoreAllInPlace = (EFI_SMM_LOCK_BOX_PARAMETER_RESTORE_ALL_IN_PLACE *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, MessageLength) + sizeof(UINT64)];
  } else {
    LockBoxParameterRestoreAllInPlace = (EFI_SMM_LOCK_BOX_PARAMETER_RESTORE_ALL_IN_PLACE *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, MessageLength) + sizeof(UINTN)];
  }
  LockBoxParameterRestoreAllInPlace->Header.Command    = EFI_SMM_LOCK_BOX_COMMAND_RESTORE_ALL_IN_PLACE;
  LockBoxParameterRestoreAllInPlace->Header.DataLength = sizeof(*LockBoxParameterRestoreAllInPlace);
  LockBoxParameterRestoreAllInPlace->Header.ReturnStatus = (UINT64)-1;

  //
  // Send command
  //
  CommSize = sizeof(CommBuffer);
  Status = SmmCommunicationPpi->Communicate (
                                  SmmCommunicationPpi,
                                  &CommBuffer[0],
                                  &CommSize
                                  );
  if (Status == EFI_NOT_STARTED) {
    //
    // Pei SMM communication not ready yet, so we access SMRAM directly
    //
    DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib Communicate - (%r)\n", Status));
    Status = InternalRestoreAllLockBoxInPlaceFromSmram ();
    LockBoxParameterRestoreAllInPlace->Header.ReturnStatus = (UINT64)Status;
  }
  ASSERT_EFI_ERROR (Status);

  Status = (EFI_STATUS)LockBoxParameterRestoreAllInPlace->Header.ReturnStatus;
  if (Status != EFI_SUCCESS) {
    // Need or MAX_BIT, because there might be case that SMM is X64 while PEI is IA32.
    Status |= MAX_BIT;
  }

  DEBUG ((DEBUG_INFO, "SmmLockBoxPeiLib RestoreAllLockBoxInPlace - Exit (%r)\n", Status));

  //
  // Done
  //
  return Status;
}

