/** @file

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

**/

#include <Uefi.h>
#include <PiDxe.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/DxeServicesLib.h>
#include <Library/PrintLib.h>

#include <Protocol/SmmCommunication.h>
#include <Protocol/SmmAccess2.h>

#include <Guid/MemoryProfile.h>
#include <Guid/PiSmmCommunicationRegionTable.h>

CHAR8  *mActionString[] = {
  "Unknown",
  "gBS->AllocatePages",
  "gBS->FreePages",
  "gBS->AllocatePool",
  "gBS->FreePool",
};

CHAR8  *mSmmActionString[] = {
  "SmmUnknown",
  "gSmst->SmmAllocatePages",
  "gSmst->SmmFreePages",
  "gSmst->SmmAllocatePool",
  "gSmst->SmmFreePool",
};

typedef struct {
  MEMORY_PROFILE_ACTION    Action;
  CHAR8                    *String;
} ACTION_STRING;

ACTION_STRING  mExtActionString[] = {
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_PAGES,                  "Lib:AllocatePages"                },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_PAGES,          "Lib:AllocateRuntimePages"         },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_PAGES,         "Lib:AllocateReservedPages"        },
  { MEMORY_PROFILE_ACTION_LIB_FREE_PAGES,                      "Lib:FreePages"                    },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_PAGES,          "Lib:AllocateAlignedPages"         },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_RUNTIME_PAGES,  "Lib:AllocateAlignedRuntimePages"  },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ALIGNED_RESERVED_PAGES, "Lib:AllocateAlignedReservedPages" },
  { MEMORY_PROFILE_ACTION_LIB_FREE_ALIGNED_PAGES,              "Lib:FreeAlignedPages"             },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_POOL,                   "Lib:AllocatePool"                 },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_POOL,           "Lib:AllocateRuntimePool"          },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_POOL,          "Lib:AllocateReservedPool"         },
  { MEMORY_PROFILE_ACTION_LIB_FREE_POOL,                       "Lib:FreePool"                     },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_ZERO_POOL,              "Lib:AllocateZeroPool"             },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_ZERO_POOL,      "Lib:AllocateRuntimeZeroPool"      },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_ZERO_POOL,     "Lib:AllocateReservedZeroPool"     },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_COPY_POOL,              "Lib:AllocateCopyPool"             },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RUNTIME_COPY_POOL,      "Lib:AllocateRuntimeCopyPool"      },
  { MEMORY_PROFILE_ACTION_LIB_ALLOCATE_RESERVED_COPY_POOL,     "Lib:AllocateReservedCopyPool"     },
  { MEMORY_PROFILE_ACTION_LIB_REALLOCATE_POOL,                 "Lib:ReallocatePool"               },
  { MEMORY_PROFILE_ACTION_LIB_REALLOCATE_RUNTIME_POOL,         "Lib:ReallocateRuntimePool"        },
  { MEMORY_PROFILE_ACTION_LIB_REALLOCATE_RESERVED_POOL,        "Lib:ReallocateReservedPool"       },
};

CHAR8  mUserDefinedActionString[] = { "UserDefined-0x80000000" };

CHAR8  *mMemoryTypeString[] = {
  "EfiReservedMemoryType",
  "EfiLoaderCode",
  "EfiLoaderData",
  "EfiBootServicesCode",
  "EfiBootServicesData",
  "EfiRuntimeServicesCode",
  "EfiRuntimeServicesData",
  "EfiConventionalMemory",
  "EfiUnusableMemory",
  "EfiACPIReclaimMemory",
  "EfiACPIMemoryNVS",
  "EfiMemoryMappedIO",
  "EfiMemoryMappedIOPortSpace",
  "EfiPalCode",
  "EfiPersistentMemory",
  "EfiOSReserved",
  "EfiOemReserved",
};

CHAR8  *mSubsystemString[] = {
  "Unknown",
  "NATIVE",
  "WINDOWS_GUI",
  "WINDOWS_CUI",
  "Unknown",
  "Unknown",
  "Unknown",
  "POSIX_CUI",
  "Unknown",
  "WINDOWS_CE_GUI",
  "EFI_APPLICATION",
  "EFI_BOOT_SERVICE_DRIVER",
  "EFI_RUNTIME_DRIVER",
  "EFI_ROM",
  "XBOX",
  "Unknown",
};

CHAR8  *mFileTypeString[] = {
  "Unknown",
  "RAW",
  "FREEFORM",
  "SECURITY_CORE",
  "PEI_CORE",
  "DXE_CORE",
  "PEIM",
  "DRIVER",
  "COMBINED_PEIM_DRIVER",
  "APPLICATION",
  "SMM",
  "FIRMWARE_VOLUME_IMAGE",
  "COMBINED_SMM_DXE",
  "SMM_CORE",
};

#define PROFILE_NAME_STRING_LENGTH  64
CHAR8  mNameString[PROFILE_NAME_STRING_LENGTH + 1];

//
// Profile summary information
//
#define MEMORY_PROFILE_ALLOC_SUMMARY_INFO_SIGNATURE  SIGNATURE_32 ('M','P','A','S')
#define MEMORY_PROFILE_ALLOC_SUMMARY_INFO_REVISION   0x0001

typedef struct {
  MEMORY_PROFILE_COMMON_HEADER    Header;
  PHYSICAL_ADDRESS                CallerAddress;
  MEMORY_PROFILE_ACTION           Action;
  CHAR8                           *ActionString;
  UINT32                          AllocateCount;
  UINT64                          TotalSize;
} MEMORY_PROFILE_ALLOC_SUMMARY_INFO;

typedef struct {
  UINT32                               Signature;
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO    AllocSummaryInfo;
  LIST_ENTRY                           Link;
} MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA;

typedef struct {
  UINT32                        Signature;
  MEMORY_PROFILE_DRIVER_INFO    *DriverInfo;
  LIST_ENTRY                    *AllocSummaryInfoList;
  LIST_ENTRY                    Link;
} MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA;

typedef struct {
  UINT32                    Signature;
  MEMORY_PROFILE_CONTEXT    *Context;
  LIST_ENTRY                *DriverSummaryInfoList;
} MEMORY_PROFILE_CONTEXT_SUMMARY_DATA;

LIST_ENTRY                           mImageSummaryQueue = INITIALIZE_LIST_HEAD_VARIABLE (mImageSummaryQueue);
MEMORY_PROFILE_CONTEXT_SUMMARY_DATA  mMemoryProfileContextSummary;

/**
  Get the file name portion of the Pdb File Name.

  The portion of the Pdb File Name between the last backslash and
  either a following period or the end of the string is copied into
  AsciiBuffer.  The name is truncated, if necessary, to ensure that
  AsciiBuffer is not overrun.

  @param[in]  PdbFileName     Pdb file name.
  @param[out] AsciiBuffer     The resultant Ascii File Name.

**/
VOID
GetShortPdbFileName (
  IN  CHAR8  *PdbFileName,
  OUT CHAR8  *AsciiBuffer
  )
{
  UINTN  IndexPdb;    // Current work location within a Pdb string.
  UINTN  IndexBuffer; // Current work location within a Buffer string.
  UINTN  StartIndex;
  UINTN  EndIndex;

  ZeroMem (AsciiBuffer, PROFILE_NAME_STRING_LENGTH + 1);

  if (PdbFileName == NULL) {
    AsciiStrnCpyS (AsciiBuffer, PROFILE_NAME_STRING_LENGTH + 1, " ", 1);
  } else {
    StartIndex = 0;
    for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++) {
    }

    for (IndexPdb = 0; PdbFileName[IndexPdb] != 0; IndexPdb++) {
      if ((PdbFileName[IndexPdb] == '\\') || (PdbFileName[IndexPdb] == '/')) {
        StartIndex = IndexPdb + 1;
      }

      if (PdbFileName[IndexPdb] == '.') {
        EndIndex = IndexPdb;
      }
    }

    IndexBuffer = 0;
    for (IndexPdb = StartIndex; IndexPdb < EndIndex; IndexPdb++) {
      AsciiBuffer[IndexBuffer] = PdbFileName[IndexPdb];
      IndexBuffer++;
      if (IndexBuffer >= PROFILE_NAME_STRING_LENGTH) {
        AsciiBuffer[PROFILE_NAME_STRING_LENGTH] = 0;
        break;
      }
    }
  }
}

/**
  Get a human readable name for an image.
  The following methods will be tried orderly:
    1. Image PDB
    2. FFS UI section
    3. Image GUID

  @param[in] DriverInfo Pointer to memory profile driver info.

  @return The resulting Ascii name string is stored in the mNameString global array.

**/
CHAR8 *
GetDriverNameString (
  IN MEMORY_PROFILE_DRIVER_INFO  *DriverInfo
  )
{
  EFI_STATUS  Status;
  CHAR16      *NameString;
  UINTN       StringSize;

  //
  // Method 1: Get the name string from image PDB
  //
  if (DriverInfo->PdbStringOffset != 0) {
    GetShortPdbFileName ((CHAR8 *)((UINTN)DriverInfo + DriverInfo->PdbStringOffset), mNameString);
    return mNameString;
  }

  if (!IsZeroGuid (&DriverInfo->FileName)) {
    //
    // Try to get the image's FFS UI section by image GUID
    //
    NameString = NULL;
    StringSize = 0;
    Status     = GetSectionFromAnyFv (
                   &DriverInfo->FileName,
                   EFI_SECTION_USER_INTERFACE,
                   0,
                   (VOID **)&NameString,
                   &StringSize
                   );
    if (!EFI_ERROR (Status)) {
      //
      // Method 2: Get the name string from FFS UI section
      //
      if (StrLen (NameString) > PROFILE_NAME_STRING_LENGTH) {
        NameString[PROFILE_NAME_STRING_LENGTH] = 0;
      }

      UnicodeStrToAsciiStrS (NameString, mNameString, sizeof (mNameString));
      FreePool (NameString);
      return mNameString;
    }
  }

  //
  // Method 3: Get the name string from image GUID
  //
  AsciiSPrint (mNameString, sizeof (mNameString), "%g", &DriverInfo->FileName);
  return mNameString;
}

/**
  Memory type to string.

  @param[in] MemoryType Memory type.

  @return Pointer to string.

**/
CHAR8 *
ProfileMemoryTypeToStr (
  IN EFI_MEMORY_TYPE  MemoryType
  )
{
  UINTN  Index;

  if ((UINT32)MemoryType >= 0x80000000) {
    //
    // OS reserved memory type.
    //
    Index = EfiMaxMemoryType;
  } else if ((UINT32)MemoryType >= 0x70000000) {
    //
    // OEM reserved memory type.
    //
    Index = EfiMaxMemoryType + 1;
  } else {
    Index = MemoryType;
  }

  return mMemoryTypeString[Index];
}

/**
  Action to string.

  @param[in] Action                     Profile action.
  @param[in] UserDefinedActionString    Pointer to user defined action string.
  @param[in] IsForSmm                   TRUE  - SMRAM profile.
                                        FALSE - UEFI memory profile.

  @return Pointer to string.

**/
CHAR8 *
ProfileActionToStr (
  IN MEMORY_PROFILE_ACTION  Action,
  IN CHAR8                  *UserDefinedActionString,
  IN BOOLEAN                IsForSmm
  )
{
  UINTN  Index;
  UINTN  ActionStringCount;
  CHAR8  **ActionString;

  if (IsForSmm) {
    ActionString      = mSmmActionString;
    ActionStringCount = ARRAY_SIZE (mSmmActionString);
  } else {
    ActionString      = mActionString;
    ActionStringCount = ARRAY_SIZE (mActionString);
  }

  if ((UINTN)(UINT32)Action < ActionStringCount) {
    return ActionString[Action];
  }

  for (Index = 0; Index < ARRAY_SIZE (mExtActionString); Index++) {
    if (mExtActionString[Index].Action == Action) {
      return mExtActionString[Index].String;
    }
  }

  if ((Action & MEMORY_PROFILE_ACTION_USER_DEFINED_MASK) != 0) {
    if (UserDefinedActionString != NULL) {
      return UserDefinedActionString;
    }

    AsciiSPrint (mUserDefinedActionString, sizeof (mUserDefinedActionString), "UserDefined-0x%08x", Action);
    return mUserDefinedActionString;
  }

  return ActionString[0];
}

/**
  Dump memory profile allocate information.

  @param[in] DriverInfo         Pointer to memory profile driver info.
  @param[in] AllocIndex         Memory profile alloc info index.
  @param[in] AllocInfo          Pointer to memory profile alloc info.
  @param[in] IsForSmm           TRUE  - SMRAM profile.
                                FALSE - UEFI memory profile.

  @return Pointer to next memory profile alloc info.

**/
MEMORY_PROFILE_ALLOC_INFO *
DumpMemoryProfileAllocInfo (
  IN MEMORY_PROFILE_DRIVER_INFO  *DriverInfo,
  IN UINTN                       AllocIndex,
  IN MEMORY_PROFILE_ALLOC_INFO   *AllocInfo,
  IN BOOLEAN                     IsForSmm
  )
{
  CHAR8  *ActionString;

  if (AllocInfo->Header.Signature != MEMORY_PROFILE_ALLOC_INFO_SIGNATURE) {
    return NULL;
  }

  if (AllocInfo->ActionStringOffset != 0) {
    ActionString = (CHAR8 *)((UINTN)AllocInfo + AllocInfo->ActionStringOffset);
  } else {
    ActionString = NULL;
  }

  Print (L"    MEMORY_PROFILE_ALLOC_INFO (0x%x)\n", AllocIndex);
  Print (L"      Signature     - 0x%08x\n", AllocInfo->Header.Signature);
  Print (L"      Length        - 0x%04x\n", AllocInfo->Header.Length);
  Print (L"      Revision      - 0x%04x\n", AllocInfo->Header.Revision);
  Print (L"      CallerAddress - 0x%016lx (Offset: 0x%08x)\n", AllocInfo->CallerAddress, (UINTN)(AllocInfo->CallerAddress - DriverInfo->ImageBase));
  Print (L"      SequenceId    - 0x%08x\n", AllocInfo->SequenceId);
  Print (L"      Action        - 0x%08x (%a)\n", AllocInfo->Action, ProfileActionToStr (AllocInfo->Action, ActionString, IsForSmm));
  Print (L"      MemoryType    - 0x%08x (%a)\n", AllocInfo->MemoryType, ProfileMemoryTypeToStr (AllocInfo->MemoryType));
  Print (L"      Buffer        - 0x%016lx\n", AllocInfo->Buffer);
  Print (L"      Size          - 0x%016lx\n", AllocInfo->Size);

  return (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)AllocInfo + AllocInfo->Header.Length);
}

/**
  Dump memory profile driver information.

  @param[in] DriverIndex        Memory profile driver info index.
  @param[in] DriverInfo         Pointer to memory profile driver info.
  @param[in] IsForSmm           TRUE  - SMRAM profile.
                                FALSE - UEFI memory profile.

  @return Pointer to next memory profile driver info.

**/
MEMORY_PROFILE_DRIVER_INFO *
DumpMemoryProfileDriverInfo (
  IN UINTN                       DriverIndex,
  IN MEMORY_PROFILE_DRIVER_INFO  *DriverInfo,
  IN BOOLEAN                     IsForSmm
  )
{
  UINTN                      TypeIndex;
  MEMORY_PROFILE_ALLOC_INFO  *AllocInfo;
  UINTN                      AllocIndex;
  CHAR8                      *NameString;

  if (DriverInfo->Header.Signature != MEMORY_PROFILE_DRIVER_INFO_SIGNATURE) {
    return NULL;
  }

  Print (L"  MEMORY_PROFILE_DRIVER_INFO (0x%x)\n", DriverIndex);
  Print (L"    Signature               - 0x%08x\n", DriverInfo->Header.Signature);
  Print (L"    Length                  - 0x%04x\n", DriverInfo->Header.Length);
  Print (L"    Revision                - 0x%04x\n", DriverInfo->Header.Revision);
  NameString = GetDriverNameString (DriverInfo);
  Print (L"    FileName                - %a\n", NameString);
  if (DriverInfo->PdbStringOffset != 0) {
    Print (L"    Pdb                     - %a\n", (CHAR8 *)((UINTN)DriverInfo + DriverInfo->PdbStringOffset));
  }

  Print (L"    ImageBase               - 0x%016lx\n", DriverInfo->ImageBase);
  Print (L"    ImageSize               - 0x%016lx\n", DriverInfo->ImageSize);
  Print (L"    EntryPoint              - 0x%016lx\n", DriverInfo->EntryPoint);
  Print (L"    ImageSubsystem          - 0x%04x (%a)\n", DriverInfo->ImageSubsystem, mSubsystemString[(DriverInfo->ImageSubsystem < sizeof (mSubsystemString)/sizeof (mSubsystemString[0])) ? DriverInfo->ImageSubsystem : 0]);
  Print (L"    FileType                - 0x%02x (%a)\n", DriverInfo->FileType, mFileTypeString[(DriverInfo->FileType < sizeof (mFileTypeString)/sizeof (mFileTypeString[0])) ? DriverInfo->FileType : 0]);
  Print (L"    CurrentUsage            - 0x%016lx\n", DriverInfo->CurrentUsage);
  Print (L"    PeakUsage               - 0x%016lx\n", DriverInfo->PeakUsage);
  for (TypeIndex = 0; TypeIndex < sizeof (DriverInfo->CurrentUsageByType) / sizeof (DriverInfo->CurrentUsageByType[0]); TypeIndex++) {
    if ((DriverInfo->CurrentUsageByType[TypeIndex] != 0) ||
        (DriverInfo->PeakUsageByType[TypeIndex] != 0))
    {
      Print (L"    CurrentUsage[0x%02x]      - 0x%016lx (%a)\n", TypeIndex, DriverInfo->CurrentUsageByType[TypeIndex], mMemoryTypeString[TypeIndex]);
      Print (L"    PeakUsage[0x%02x]         - 0x%016lx (%a)\n", TypeIndex, DriverInfo->PeakUsageByType[TypeIndex], mMemoryTypeString[TypeIndex]);
    }
  }

  Print (L"    AllocRecordCount        - 0x%08x\n", DriverInfo->AllocRecordCount);

  AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)DriverInfo + DriverInfo->Header.Length);
  for (AllocIndex = 0; AllocIndex < DriverInfo->AllocRecordCount; AllocIndex++) {
    AllocInfo = DumpMemoryProfileAllocInfo (DriverInfo, AllocIndex, AllocInfo, IsForSmm);
    if (AllocInfo == NULL) {
      return NULL;
    }
  }

  return (MEMORY_PROFILE_DRIVER_INFO *)AllocInfo;
}

/**
  Dump memory profile context information.

  @param[in] Context            Pointer to memory profile context.
  @param[in] IsForSmm           TRUE  - SMRAM profile.
                                FALSE - UEFI memory profile.

  @return Pointer to the end of memory profile context buffer.

**/
VOID *
DumpMemoryProfileContext (
  IN MEMORY_PROFILE_CONTEXT  *Context,
  IN BOOLEAN                 IsForSmm
  )
{
  UINTN                       TypeIndex;
  MEMORY_PROFILE_DRIVER_INFO  *DriverInfo;
  UINTN                       DriverIndex;

  if (Context->Header.Signature != MEMORY_PROFILE_CONTEXT_SIGNATURE) {
    return NULL;
  }

  Print (L"MEMORY_PROFILE_CONTEXT\n");
  Print (L"  Signature                     - 0x%08x\n", Context->Header.Signature);
  Print (L"  Length                        - 0x%04x\n", Context->Header.Length);
  Print (L"  Revision                      - 0x%04x\n", Context->Header.Revision);
  Print (L"  CurrentTotalUsage             - 0x%016lx\n", Context->CurrentTotalUsage);
  Print (L"  PeakTotalUsage                - 0x%016lx\n", Context->PeakTotalUsage);
  for (TypeIndex = 0; TypeIndex < sizeof (Context->CurrentTotalUsageByType) / sizeof (Context->CurrentTotalUsageByType[0]); TypeIndex++) {
    if ((Context->CurrentTotalUsageByType[TypeIndex] != 0) ||
        (Context->PeakTotalUsageByType[TypeIndex] != 0))
    {
      Print (L"  CurrentTotalUsage[0x%02x]       - 0x%016lx (%a)\n", TypeIndex, Context->CurrentTotalUsageByType[TypeIndex], mMemoryTypeString[TypeIndex]);
      Print (L"  PeakTotalUsage[0x%02x]          - 0x%016lx (%a)\n", TypeIndex, Context->PeakTotalUsageByType[TypeIndex], mMemoryTypeString[TypeIndex]);
    }
  }

  Print (L"  TotalImageSize                - 0x%016lx\n", Context->TotalImageSize);
  Print (L"  ImageCount                    - 0x%08x\n", Context->ImageCount);
  Print (L"  SequenceCount                 - 0x%08x\n", Context->SequenceCount);

  DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)((UINTN)Context + Context->Header.Length);
  for (DriverIndex = 0; DriverIndex < Context->ImageCount; DriverIndex++) {
    DriverInfo = DumpMemoryProfileDriverInfo (DriverIndex, DriverInfo, IsForSmm);
    if (DriverInfo == NULL) {
      return NULL;
    }
  }

  return (VOID *)DriverInfo;
}

/**
  Dump memory profile descriptor information.

  @param[in] DescriptorIndex    Memory profile descriptor index.
  @param[in] Descriptor         Pointer to memory profile descriptor.

  @return Pointer to next memory profile descriptor.

**/
MEMORY_PROFILE_DESCRIPTOR *
DumpMemoryProfileDescriptor (
  IN UINTN                      DescriptorIndex,
  IN MEMORY_PROFILE_DESCRIPTOR  *Descriptor
  )
{
  if (Descriptor->Header.Signature != MEMORY_PROFILE_DESCRIPTOR_SIGNATURE) {
    return NULL;
  }

  Print (L"  MEMORY_PROFILE_DESCRIPTOR (0x%x)\n", DescriptorIndex);
  Print (L"    Signature               - 0x%08x\n", Descriptor->Header.Signature);
  Print (L"    Length                  - 0x%04x\n", Descriptor->Header.Length);
  Print (L"    Revision                - 0x%04x\n", Descriptor->Header.Revision);
  Print (L"    Address                 - 0x%016lx\n", Descriptor->Address);
  Print (L"    Size                    - 0x%016lx\n", Descriptor->Size);

  return (MEMORY_PROFILE_DESCRIPTOR *)((UINTN)Descriptor + Descriptor->Header.Length);
}

/**
  Dump memory profile free memory information.

  @param[in] FreeMemory         Pointer to memory profile free memory.

  @return Pointer to the end of memory profile free memory buffer.

**/
VOID *
DumpMemoryProfileFreeMemory (
  IN MEMORY_PROFILE_FREE_MEMORY  *FreeMemory
  )
{
  MEMORY_PROFILE_DESCRIPTOR  *Descriptor;
  UINTN                      DescriptorIndex;

  if (FreeMemory->Header.Signature != MEMORY_PROFILE_FREE_MEMORY_SIGNATURE) {
    return NULL;
  }

  Print (L"MEMORY_PROFILE_FREE_MEMORY\n");
  Print (L"  Signature                     - 0x%08x\n", FreeMemory->Header.Signature);
  Print (L"  Length                        - 0x%04x\n", FreeMemory->Header.Length);
  Print (L"  Revision                      - 0x%04x\n", FreeMemory->Header.Revision);
  Print (L"  TotalFreeMemoryPages          - 0x%016lx\n", FreeMemory->TotalFreeMemoryPages);
  Print (L"  FreeMemoryEntryCount          - 0x%08x\n", FreeMemory->FreeMemoryEntryCount);

  Descriptor = (MEMORY_PROFILE_DESCRIPTOR *)((UINTN)FreeMemory + FreeMemory->Header.Length);
  for (DescriptorIndex = 0; DescriptorIndex < FreeMemory->FreeMemoryEntryCount; DescriptorIndex++) {
    Descriptor = DumpMemoryProfileDescriptor (DescriptorIndex, Descriptor);
    if (Descriptor == NULL) {
      return NULL;
    }
  }

  return (VOID *)Descriptor;
}

/**
  Dump memory profile memory range information.

  @param[in] MemoryRange        Pointer to memory profile memory range.

  @return Pointer to the end of memory profile memory range buffer.

**/
VOID *
DumpMemoryProfileMemoryRange (
  IN MEMORY_PROFILE_MEMORY_RANGE  *MemoryRange
  )
{
  MEMORY_PROFILE_DESCRIPTOR  *Descriptor;
  UINTN                      DescriptorIndex;

  if (MemoryRange->Header.Signature != MEMORY_PROFILE_MEMORY_RANGE_SIGNATURE) {
    return NULL;
  }

  Print (L"MEMORY_PROFILE_MEMORY_RANGE\n");
  Print (L"  Signature                     - 0x%08x\n", MemoryRange->Header.Signature);
  Print (L"  Length                        - 0x%04x\n", MemoryRange->Header.Length);
  Print (L"  Revision                      - 0x%04x\n", MemoryRange->Header.Revision);
  Print (L"  MemoryRangeCount              - 0x%08x\n", MemoryRange->MemoryRangeCount);

  Descriptor = (MEMORY_PROFILE_DESCRIPTOR *)((UINTN)MemoryRange + MemoryRange->Header.Length);
  for (DescriptorIndex = 0; DescriptorIndex < MemoryRange->MemoryRangeCount; DescriptorIndex++) {
    Descriptor = DumpMemoryProfileDescriptor (DescriptorIndex, Descriptor);
    if (Descriptor == NULL) {
      return NULL;
    }
  }

  return (VOID *)Descriptor;
}

/**
  Scan memory profile by Signature.

  @param[in] ProfileBuffer      Memory profile base address.
  @param[in] ProfileSize        Memory profile size.
  @param[in] Signature          Signature.

  @return Pointer to the structure with the signature.

**/
VOID *
ScanMemoryProfileBySignature (
  IN PHYSICAL_ADDRESS  ProfileBuffer,
  IN UINT64            ProfileSize,
  IN UINT32            Signature
  )
{
  MEMORY_PROFILE_COMMON_HEADER  *CommonHeader;
  UINTN                         ProfileEnd;

  ProfileEnd   = (UINTN)(ProfileBuffer + ProfileSize);
  CommonHeader = (MEMORY_PROFILE_COMMON_HEADER *)(UINTN)ProfileBuffer;
  while ((UINTN)CommonHeader < ProfileEnd) {
    if (CommonHeader->Signature == Signature) {
      //
      // Found it.
      //
      return (VOID *)CommonHeader;
    }

    if (CommonHeader->Length == 0) {
      ASSERT (FALSE);
      return NULL;
    }

    CommonHeader = (MEMORY_PROFILE_COMMON_HEADER *)((UINTN)CommonHeader + CommonHeader->Length);
  }

  return NULL;
}

/**
  Dump memory profile information.

  @param[in] ProfileBuffer      Memory profile base address.
  @param[in] ProfileSize        Memory profile size.
  @param[in] IsForSmm           TRUE  - SMRAM profile.
                                FALSE - UEFI memory profile.

**/
VOID
DumpMemoryProfile (
  IN PHYSICAL_ADDRESS  ProfileBuffer,
  IN UINT64            ProfileSize,
  IN BOOLEAN           IsForSmm
  )
{
  MEMORY_PROFILE_CONTEXT       *Context;
  MEMORY_PROFILE_FREE_MEMORY   *FreeMemory;
  MEMORY_PROFILE_MEMORY_RANGE  *MemoryRange;

  Context = (MEMORY_PROFILE_CONTEXT *)ScanMemoryProfileBySignature (ProfileBuffer, ProfileSize, MEMORY_PROFILE_CONTEXT_SIGNATURE);
  if (Context != NULL) {
    DumpMemoryProfileContext (Context, IsForSmm);
  }

  FreeMemory = (MEMORY_PROFILE_FREE_MEMORY *)ScanMemoryProfileBySignature (ProfileBuffer, ProfileSize, MEMORY_PROFILE_FREE_MEMORY_SIGNATURE);
  if (FreeMemory != NULL) {
    DumpMemoryProfileFreeMemory (FreeMemory);
  }

  MemoryRange = (MEMORY_PROFILE_MEMORY_RANGE *)ScanMemoryProfileBySignature (ProfileBuffer, ProfileSize, MEMORY_PROFILE_MEMORY_RANGE_SIGNATURE);
  if (MemoryRange != NULL) {
    DumpMemoryProfileMemoryRange (MemoryRange);
  }
}

/**
  Get Allocate summary information structure by caller address.

  @param[in] CallerAddress          Caller address.
  @param[in] DriverSummaryInfoData  Driver summary information data structure.

  @return Allocate summary information structure by caller address.

**/
MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA *
GetAllocSummaryInfoByCallerAddress (
  IN PHYSICAL_ADDRESS                         CallerAddress,
  IN MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA  *DriverSummaryInfoData
  )
{
  LIST_ENTRY                              *AllocSummaryInfoList;
  LIST_ENTRY                              *AllocSummaryLink;
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO       *AllocSummaryInfo;
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA  *AllocSummaryInfoData;

  AllocSummaryInfoList = DriverSummaryInfoData->AllocSummaryInfoList;

  for (AllocSummaryLink = AllocSummaryInfoList->ForwardLink;
       AllocSummaryLink != AllocSummaryInfoList;
       AllocSummaryLink = AllocSummaryLink->ForwardLink)
  {
    AllocSummaryInfoData = CR (
                             AllocSummaryLink,
                             MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA,
                             Link,
                             MEMORY_PROFILE_ALLOC_SUMMARY_INFO_SIGNATURE
                             );
    AllocSummaryInfo = &AllocSummaryInfoData->AllocSummaryInfo;
    if (AllocSummaryInfo->CallerAddress == CallerAddress) {
      return AllocSummaryInfoData;
    }
  }

  return NULL;
}

/**
  Create Allocate summary information structure and
  link to Driver summary information data structure.

  @param[in, out] DriverSummaryInfoData Driver summary information data structure.
  @param[in]      AllocInfo             Pointer to memory profile alloc info.

  @return Pointer to next memory profile alloc info.

**/
MEMORY_PROFILE_ALLOC_INFO *
CreateAllocSummaryInfo (
  IN OUT MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA  *DriverSummaryInfoData,
  IN MEMORY_PROFILE_ALLOC_INFO                    *AllocInfo
  )
{
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA  *AllocSummaryInfoData;
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO       *AllocSummaryInfo;

  if (AllocInfo->Header.Signature != MEMORY_PROFILE_ALLOC_INFO_SIGNATURE) {
    return NULL;
  }

  AllocSummaryInfoData = GetAllocSummaryInfoByCallerAddress (AllocInfo->CallerAddress, DriverSummaryInfoData);
  if (AllocSummaryInfoData == NULL) {
    AllocSummaryInfoData = AllocatePool (sizeof (*AllocSummaryInfoData));
    if (AllocSummaryInfoData == NULL) {
      return NULL;
    }

    AllocSummaryInfoData->Signature    = MEMORY_PROFILE_ALLOC_SUMMARY_INFO_SIGNATURE;
    AllocSummaryInfo                   = &AllocSummaryInfoData->AllocSummaryInfo;
    AllocSummaryInfo->Header.Signature = MEMORY_PROFILE_ALLOC_SUMMARY_INFO_SIGNATURE;
    AllocSummaryInfo->Header.Length    = sizeof (*AllocSummaryInfo);
    AllocSummaryInfo->Header.Revision  = MEMORY_PROFILE_ALLOC_SUMMARY_INFO_REVISION;
    AllocSummaryInfo->CallerAddress    = AllocInfo->CallerAddress;
    AllocSummaryInfo->Action           = AllocInfo->Action;
    if (AllocInfo->ActionStringOffset != 0) {
      AllocSummaryInfo->ActionString = (CHAR8 *)((UINTN)AllocInfo + AllocInfo->ActionStringOffset);
    } else {
      AllocSummaryInfo->ActionString = NULL;
    }

    AllocSummaryInfo->AllocateCount = 0;
    AllocSummaryInfo->TotalSize     = 0;
    InsertTailList (DriverSummaryInfoData->AllocSummaryInfoList, &AllocSummaryInfoData->Link);
  }

  AllocSummaryInfo = &AllocSummaryInfoData->AllocSummaryInfo;
  AllocSummaryInfo->AllocateCount++;
  AllocSummaryInfo->TotalSize += AllocInfo->Size;

  return (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)AllocInfo + AllocInfo->Header.Length);
}

/**
  Create Driver summary information structure and
  link to Context summary information data structure.

  @param[in, out] ContextSummaryData    Context summary information data structure.
  @param[in]      DriverInfo            Pointer to memory profile driver info.

  @return Pointer to next memory profile driver info.

**/
MEMORY_PROFILE_DRIVER_INFO *
CreateDriverSummaryInfo (
  IN OUT MEMORY_PROFILE_CONTEXT_SUMMARY_DATA  *ContextSummaryData,
  IN MEMORY_PROFILE_DRIVER_INFO               *DriverInfo
  )
{
  MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA  *DriverSummaryInfoData;
  MEMORY_PROFILE_ALLOC_INFO                *AllocInfo;
  UINTN                                    AllocIndex;

  if (DriverInfo->Header.Signature != MEMORY_PROFILE_DRIVER_INFO_SIGNATURE) {
    return NULL;
  }

  DriverSummaryInfoData = AllocatePool (sizeof (*DriverSummaryInfoData) + sizeof (LIST_ENTRY));
  if (DriverSummaryInfoData == NULL) {
    return NULL;
  }

  DriverSummaryInfoData->Signature            = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE;
  DriverSummaryInfoData->DriverInfo           = DriverInfo;
  DriverSummaryInfoData->AllocSummaryInfoList = (LIST_ENTRY *)(DriverSummaryInfoData + 1);
  InitializeListHead (DriverSummaryInfoData->AllocSummaryInfoList);
  InsertTailList (ContextSummaryData->DriverSummaryInfoList, &DriverSummaryInfoData->Link);

  AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)DriverInfo + DriverInfo->Header.Length);
  for (AllocIndex = 0; AllocIndex < DriverInfo->AllocRecordCount; AllocIndex++) {
    AllocInfo = CreateAllocSummaryInfo (DriverSummaryInfoData, AllocInfo);
    if (AllocInfo == NULL) {
      return NULL;
    }
  }

  return (MEMORY_PROFILE_DRIVER_INFO *)AllocInfo;
}

/**
  Create Context summary information structure.

  @param[in] ProfileBuffer      Memory profile base address.
  @param[in] ProfileSize        Memory profile size.

  @return Context summary information structure.

**/
MEMORY_PROFILE_CONTEXT_SUMMARY_DATA *
CreateContextSummaryData (
  IN PHYSICAL_ADDRESS  ProfileBuffer,
  IN UINT64            ProfileSize
  )
{
  MEMORY_PROFILE_CONTEXT      *Context;
  MEMORY_PROFILE_DRIVER_INFO  *DriverInfo;
  UINTN                       DriverIndex;

  Context = (MEMORY_PROFILE_CONTEXT *)ScanMemoryProfileBySignature (ProfileBuffer, ProfileSize, MEMORY_PROFILE_CONTEXT_SIGNATURE);
  if (Context == NULL) {
    return NULL;
  }

  mMemoryProfileContextSummary.Signature             = MEMORY_PROFILE_CONTEXT_SIGNATURE;
  mMemoryProfileContextSummary.Context               = Context;
  mMemoryProfileContextSummary.DriverSummaryInfoList = &mImageSummaryQueue;

  DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)((UINTN)Context + Context->Header.Length);
  for (DriverIndex = 0; DriverIndex < Context->ImageCount; DriverIndex++) {
    DriverInfo = CreateDriverSummaryInfo (&mMemoryProfileContextSummary, DriverInfo);
    if (DriverInfo == NULL) {
      return NULL;
    }
  }

  return &mMemoryProfileContextSummary;
}

/**
  Dump Context summary information.

  @param[in] ContextSummaryData Context summary information data.
  @param[in] IsForSmm           TRUE  - SMRAM profile.
                                FALSE - UEFI memory profile.

**/
VOID
DumpContextSummaryData (
  IN MEMORY_PROFILE_CONTEXT_SUMMARY_DATA  *ContextSummaryData,
  IN BOOLEAN                              IsForSmm
  )
{
  MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA  *DriverSummaryInfoData;
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA   *AllocSummaryInfoData;
  LIST_ENTRY                               *DriverSummaryInfoList;
  LIST_ENTRY                               *DriverSummaryLink;
  LIST_ENTRY                               *AllocSummaryInfoList;
  LIST_ENTRY                               *AllocSummaryLink;
  MEMORY_PROFILE_DRIVER_INFO               *DriverInfo;
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO        *AllocSummaryInfo;
  CHAR8                                    *NameString;

  if (ContextSummaryData == NULL) {
    return;
  }

  Print (L"\nSummary Data:\n");

  DriverSummaryInfoList = ContextSummaryData->DriverSummaryInfoList;
  for (DriverSummaryLink = DriverSummaryInfoList->ForwardLink;
       DriverSummaryLink != DriverSummaryInfoList;
       DriverSummaryLink = DriverSummaryLink->ForwardLink)
  {
    DriverSummaryInfoData = CR (
                              DriverSummaryLink,
                              MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA,
                              Link,
                              MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
                              );
    DriverInfo = DriverSummaryInfoData->DriverInfo;

    NameString = GetDriverNameString (DriverInfo);
    Print (L"\nDriver - %a (Usage - 0x%08x)", NameString, DriverInfo->CurrentUsage);
    if (DriverInfo->CurrentUsage == 0) {
      Print (L"\n");
      continue;
    }

    if (DriverInfo->PdbStringOffset != 0) {
      Print (L" (Pdb - %a)\n", (CHAR8 *)((UINTN)DriverInfo + DriverInfo->PdbStringOffset));
    } else {
      Print (L"\n");
    }

    Print (L"Caller List:\n");
    Print (L"  Count            Size                   RVA              Action\n");
    Print (L"==========  ==================     ================== (================================)\n");
    AllocSummaryInfoList = DriverSummaryInfoData->AllocSummaryInfoList;
    for (AllocSummaryLink = AllocSummaryInfoList->ForwardLink;
         AllocSummaryLink != AllocSummaryInfoList;
         AllocSummaryLink = AllocSummaryLink->ForwardLink)
    {
      AllocSummaryInfoData = CR (
                               AllocSummaryLink,
                               MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA,
                               Link,
                               MEMORY_PROFILE_ALLOC_SUMMARY_INFO_SIGNATURE
                               );
      AllocSummaryInfo = &AllocSummaryInfoData->AllocSummaryInfo;

      Print (
        L"0x%08x  0x%016lx <== 0x%016lx",
        AllocSummaryInfo->AllocateCount,
        AllocSummaryInfo->TotalSize,
        AllocSummaryInfo->CallerAddress - DriverInfo->ImageBase
        );
      Print (L" (%a)\n", ProfileActionToStr (AllocSummaryInfo->Action, AllocSummaryInfo->ActionString, IsForSmm));
    }
  }

  return;
}

/**
  Destroy Context summary information.

  @param[in, out] ContextSummaryData    Context summary information data.

**/
VOID
DestroyContextSummaryData (
  IN OUT MEMORY_PROFILE_CONTEXT_SUMMARY_DATA  *ContextSummaryData
  )
{
  MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA  *DriverSummaryInfoData;
  MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA   *AllocSummaryInfoData;
  LIST_ENTRY                               *DriverSummaryInfoList;
  LIST_ENTRY                               *DriverSummaryLink;
  LIST_ENTRY                               *AllocSummaryInfoList;
  LIST_ENTRY                               *AllocSummaryLink;

  if (ContextSummaryData == NULL) {
    return;
  }

  DriverSummaryInfoList = ContextSummaryData->DriverSummaryInfoList;
  for (DriverSummaryLink = DriverSummaryInfoList->ForwardLink;
       DriverSummaryLink != DriverSummaryInfoList;
       )
  {
    DriverSummaryInfoData = CR (
                              DriverSummaryLink,
                              MEMORY_PROFILE_DRIVER_SUMMARY_INFO_DATA,
                              Link,
                              MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
                              );
    DriverSummaryLink = DriverSummaryLink->ForwardLink;

    AllocSummaryInfoList = DriverSummaryInfoData->AllocSummaryInfoList;
    for (AllocSummaryLink = AllocSummaryInfoList->ForwardLink;
         AllocSummaryLink != AllocSummaryInfoList;
         )
    {
      AllocSummaryInfoData = CR (
                               AllocSummaryLink,
                               MEMORY_PROFILE_ALLOC_SUMMARY_INFO_DATA,
                               Link,
                               MEMORY_PROFILE_ALLOC_SUMMARY_INFO_SIGNATURE
                               );
      AllocSummaryLink = AllocSummaryLink->ForwardLink;

      RemoveEntryList (&AllocSummaryInfoData->Link);
      FreePool (AllocSummaryInfoData);
    }

    RemoveEntryList (&DriverSummaryInfoData->Link);
    FreePool (DriverSummaryInfoData);
  }

  return;
}

/**
  Get and dump UEFI memory profile data.

  @return EFI_SUCCESS   Get the memory profile data successfully.
  @return other         Fail to get the memory profile data.

**/
EFI_STATUS
GetUefiMemoryProfileData (
  VOID
  )
{
  EFI_STATUS                           Status;
  EDKII_MEMORY_PROFILE_PROTOCOL        *ProfileProtocol;
  VOID                                 *Data;
  UINT64                               Size;
  MEMORY_PROFILE_CONTEXT_SUMMARY_DATA  *MemoryProfileContextSummaryData;
  BOOLEAN                              RecordingState;

  Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID **)&ProfileProtocol);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "UefiMemoryProfile: Locate MemoryProfile protocol - %r\n", Status));
    return Status;
  }

  //
  // Set recording state if needed.
  //
  RecordingState = MEMORY_PROFILE_RECORDING_DISABLE;
  Status         = ProfileProtocol->GetRecordingState (ProfileProtocol, &RecordingState);
  if (RecordingState == MEMORY_PROFILE_RECORDING_ENABLE) {
    ProfileProtocol->SetRecordingState (ProfileProtocol, MEMORY_PROFILE_RECORDING_DISABLE);
  }

  Size   = 0;
  Data   = NULL;
  Status = ProfileProtocol->GetData (
                              ProfileProtocol,
                              &Size,
                              Data
                              );
  if (Status != EFI_BUFFER_TOO_SMALL) {
    Print (L"UefiMemoryProfile: GetData - %r\n", Status);
    goto Done;
  }

  Data = AllocateZeroPool ((UINTN)Size);
  if (Data == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    Print (L"UefiMemoryProfile: AllocateZeroPool (0x%x) - %r\n", Size, Status);
    return Status;
  }

  Status = ProfileProtocol->GetData (
                              ProfileProtocol,
                              &Size,
                              Data
                              );
  if (EFI_ERROR (Status)) {
    Print (L"UefiMemoryProfile: GetData - %r\n", Status);
    goto Done;
  }

  Print (L"UefiMemoryProfileSize - 0x%x\n", Size);
  Print (L"======= UefiMemoryProfile begin =======\n");
  DumpMemoryProfile ((PHYSICAL_ADDRESS)(UINTN)Data, Size, FALSE);

  //
  // Dump summary information
  //
  MemoryProfileContextSummaryData = CreateContextSummaryData ((PHYSICAL_ADDRESS)(UINTN)Data, Size);
  if (MemoryProfileContextSummaryData != NULL) {
    DumpContextSummaryData (MemoryProfileContextSummaryData, FALSE);
    DestroyContextSummaryData (MemoryProfileContextSummaryData);
  }

  Print (L"======= UefiMemoryProfile end =======\n\n\n");

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

  //
  // Restore recording state if needed.
  //
  if (RecordingState == MEMORY_PROFILE_RECORDING_ENABLE) {
    ProfileProtocol->SetRecordingState (ProfileProtocol, MEMORY_PROFILE_RECORDING_ENABLE);
  }

  return Status;
}

/**
  Get and dump SMRAM profile data.

  @return EFI_SUCCESS   Get the SMRAM profile data successfully.
  @return other         Fail to get the SMRAM profile data.

**/
EFI_STATUS
GetSmramProfileData (
  VOID
  )
{
  EFI_STATUS                                          Status;
  UINTN                                               CommSize;
  UINT8                                               *CommBuffer;
  EFI_SMM_COMMUNICATE_HEADER                          *CommHeader;
  SMRAM_PROFILE_PARAMETER_GET_PROFILE_INFO            *CommGetProfileInfo;
  SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET  *CommGetProfileData;
  SMRAM_PROFILE_PARAMETER_RECORDING_STATE             *CommRecordingState;
  UINTN                                               ProfileSize;
  VOID                                                *ProfileBuffer;
  EFI_SMM_COMMUNICATION_PROTOCOL                      *SmmCommunication;
  UINTN                                               MinimalSizeNeeded;
  EDKII_PI_SMM_COMMUNICATION_REGION_TABLE             *PiSmmCommunicationRegionTable;
  UINT32                                              Index;
  EFI_MEMORY_DESCRIPTOR                               *Entry;
  VOID                                                *Buffer;
  UINTN                                               Size;
  UINTN                                               Offset;
  MEMORY_PROFILE_CONTEXT_SUMMARY_DATA                 *MemoryProfileContextSummaryData;
  BOOLEAN                                             RecordingState;

  ProfileBuffer = NULL;

  Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **)&SmmCommunication);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "SmramProfile: Locate SmmCommunication protocol - %r\n", Status));
    return Status;
  }

  MinimalSizeNeeded = sizeof (EFI_GUID) +
                      sizeof (UINTN) +
                      MAX (
                        sizeof (SMRAM_PROFILE_PARAMETER_GET_PROFILE_INFO),
                        MAX (
                          sizeof (SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET),
                          sizeof (SMRAM_PROFILE_PARAMETER_RECORDING_STATE)
                          )
                        );
  MinimalSizeNeeded += MAX (
                         sizeof (MEMORY_PROFILE_CONTEXT),
                         MAX (
                           sizeof (MEMORY_PROFILE_DRIVER_INFO),
                           MAX (
                             sizeof (MEMORY_PROFILE_ALLOC_INFO),
                             MAX (
                               sizeof (MEMORY_PROFILE_DESCRIPTOR),
                               MAX (
                                 sizeof (MEMORY_PROFILE_FREE_MEMORY),
                                 sizeof (MEMORY_PROFILE_MEMORY_RANGE)
                                 )
                               )
                             )
                           )
                         );

  Status = EfiGetSystemConfigurationTable (
             &gEdkiiPiSmmCommunicationRegionTableGuid,
             (VOID **)&PiSmmCommunicationRegionTable
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "SmramProfile: Get PiSmmCommunicationRegionTable - %r\n", Status));
    return Status;
  }

  ASSERT (PiSmmCommunicationRegionTable != NULL);
  Entry = (EFI_MEMORY_DESCRIPTOR *)(PiSmmCommunicationRegionTable + 1);
  Size  = 0;
  for (Index = 0; Index < PiSmmCommunicationRegionTable->NumberOfEntries; Index++) {
    if (Entry->Type == EfiConventionalMemory) {
      Size = EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages);
      if (Size >= MinimalSizeNeeded) {
        break;
      }
    }

    Entry = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)Entry + PiSmmCommunicationRegionTable->DescriptorSize);
  }

  ASSERT (Index < PiSmmCommunicationRegionTable->NumberOfEntries);
  CommBuffer = (UINT8 *)(UINTN)Entry->PhysicalStart;

  //
  // Set recording state if needed.
  //
  RecordingState = MEMORY_PROFILE_RECORDING_DISABLE;

  CommHeader = (EFI_SMM_COMMUNICATE_HEADER *)&CommBuffer[0];
  CopyMem (&CommHeader->HeaderGuid, &gEdkiiMemoryProfileGuid, sizeof (gEdkiiMemoryProfileGuid));
  CommHeader->MessageLength = sizeof (SMRAM_PROFILE_PARAMETER_RECORDING_STATE);

  CommRecordingState                      = (SMRAM_PROFILE_PARAMETER_RECORDING_STATE *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)];
  CommRecordingState->Header.Command      = SMRAM_PROFILE_COMMAND_GET_RECORDING_STATE;
  CommRecordingState->Header.DataLength   = sizeof (*CommRecordingState);
  CommRecordingState->Header.ReturnStatus = (UINT64)-1;
  CommRecordingState->RecordingState      = MEMORY_PROFILE_RECORDING_DISABLE;

  CommSize = sizeof (EFI_GUID) + sizeof (UINTN) + CommHeader->MessageLength;
  Status   = SmmCommunication->Communicate (SmmCommunication, CommBuffer, &CommSize);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "SmramProfile: SmmCommunication - %r\n", Status));
    return Status;
  }

  if (CommRecordingState->Header.ReturnStatus != 0) {
    Print (L"SmramProfile: GetRecordingState - 0x%0x\n", CommRecordingState->Header.ReturnStatus);
    return EFI_SUCCESS;
  }

  RecordingState = CommRecordingState->RecordingState;
  if (RecordingState == MEMORY_PROFILE_RECORDING_ENABLE) {
    CommHeader = (EFI_SMM_COMMUNICATE_HEADER *)&CommBuffer[0];
    CopyMem (&CommHeader->HeaderGuid, &gEdkiiMemoryProfileGuid, sizeof (gEdkiiMemoryProfileGuid));
    CommHeader->MessageLength = sizeof (SMRAM_PROFILE_PARAMETER_RECORDING_STATE);

    CommRecordingState                      = (SMRAM_PROFILE_PARAMETER_RECORDING_STATE *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)];
    CommRecordingState->Header.Command      = SMRAM_PROFILE_COMMAND_SET_RECORDING_STATE;
    CommRecordingState->Header.DataLength   = sizeof (*CommRecordingState);
    CommRecordingState->Header.ReturnStatus = (UINT64)-1;
    CommRecordingState->RecordingState      = MEMORY_PROFILE_RECORDING_DISABLE;

    CommSize = sizeof (EFI_GUID) + sizeof (UINTN) + CommHeader->MessageLength;
    SmmCommunication->Communicate (SmmCommunication, CommBuffer, &CommSize);
  }

  //
  // Get Size
  //
  CommHeader = (EFI_SMM_COMMUNICATE_HEADER *)&CommBuffer[0];
  CopyMem (&CommHeader->HeaderGuid, &gEdkiiMemoryProfileGuid, sizeof (gEdkiiMemoryProfileGuid));
  CommHeader->MessageLength = sizeof (SMRAM_PROFILE_PARAMETER_GET_PROFILE_INFO);

  CommGetProfileInfo                      = (SMRAM_PROFILE_PARAMETER_GET_PROFILE_INFO *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)];
  CommGetProfileInfo->Header.Command      = SMRAM_PROFILE_COMMAND_GET_PROFILE_INFO;
  CommGetProfileInfo->Header.DataLength   = sizeof (*CommGetProfileInfo);
  CommGetProfileInfo->Header.ReturnStatus = (UINT64)-1;
  CommGetProfileInfo->ProfileSize         = 0;

  CommSize = sizeof (EFI_GUID) + sizeof (UINTN) + CommHeader->MessageLength;
  Status   = SmmCommunication->Communicate (SmmCommunication, CommBuffer, &CommSize);
  ASSERT_EFI_ERROR (Status);

  if (CommGetProfileInfo->Header.ReturnStatus != 0) {
    Status = EFI_SUCCESS;
    Print (L"SmramProfile: GetProfileInfo - 0x%0x\n", CommGetProfileInfo->Header.ReturnStatus);
    goto Done;
  }

  ProfileSize = (UINTN)CommGetProfileInfo->ProfileSize;

  //
  // Get Data
  //
  ProfileBuffer = AllocateZeroPool (ProfileSize);
  if (ProfileBuffer == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    Print (L"SmramProfile: AllocateZeroPool (0x%x) for profile buffer - %r\n", ProfileSize, Status);
    goto Done;
  }

  CommHeader = (EFI_SMM_COMMUNICATE_HEADER *)&CommBuffer[0];
  CopyMem (&CommHeader->HeaderGuid, &gEdkiiMemoryProfileGuid, sizeof (gEdkiiMemoryProfileGuid));
  CommHeader->MessageLength = sizeof (SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET);

  CommGetProfileData                      = (SMRAM_PROFILE_PARAMETER_GET_PROFILE_DATA_BY_OFFSET *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)];
  CommGetProfileData->Header.Command      = SMRAM_PROFILE_COMMAND_GET_PROFILE_DATA_BY_OFFSET;
  CommGetProfileData->Header.DataLength   = sizeof (*CommGetProfileData);
  CommGetProfileData->Header.ReturnStatus = (UINT64)-1;

  CommSize = sizeof (EFI_GUID) + sizeof (UINTN) + CommHeader->MessageLength;
  Buffer   = (UINT8 *)CommHeader + CommSize;
  Size    -= CommSize;

  CommGetProfileData->ProfileBuffer = (PHYSICAL_ADDRESS)(UINTN)Buffer;
  CommGetProfileData->ProfileOffset = 0;
  while (CommGetProfileData->ProfileOffset < ProfileSize) {
    Offset = (UINTN)CommGetProfileData->ProfileOffset;
    if (Size <= (ProfileSize - CommGetProfileData->ProfileOffset)) {
      CommGetProfileData->ProfileSize = (UINT64)Size;
    } else {
      CommGetProfileData->ProfileSize = (UINT64)(ProfileSize - CommGetProfileData->ProfileOffset);
    }

    Status = SmmCommunication->Communicate (SmmCommunication, CommBuffer, &CommSize);
    ASSERT_EFI_ERROR (Status);

    if (CommGetProfileData->Header.ReturnStatus != 0) {
      Status = EFI_SUCCESS;
      Print (L"GetProfileData - 0x%x\n", CommGetProfileData->Header.ReturnStatus);
      goto Done;
    }

    CopyMem ((UINT8 *)ProfileBuffer + Offset, (VOID *)(UINTN)CommGetProfileData->ProfileBuffer, (UINTN)CommGetProfileData->ProfileSize);
  }

  Print (L"SmramProfileSize - 0x%x\n", ProfileSize);
  Print (L"======= SmramProfile begin =======\n");
  DumpMemoryProfile ((PHYSICAL_ADDRESS)(UINTN)ProfileBuffer, ProfileSize, TRUE);

  //
  // Dump summary information
  //
  MemoryProfileContextSummaryData = CreateContextSummaryData ((PHYSICAL_ADDRESS)(UINTN)ProfileBuffer, ProfileSize);
  if (MemoryProfileContextSummaryData != NULL) {
    DumpContextSummaryData (MemoryProfileContextSummaryData, TRUE);
    DestroyContextSummaryData (MemoryProfileContextSummaryData);
  }

  Print (L"======= SmramProfile end =======\n\n\n");

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

  //
  // Restore recording state if needed.
  //
  if (RecordingState == MEMORY_PROFILE_RECORDING_ENABLE) {
    CommHeader = (EFI_SMM_COMMUNICATE_HEADER *)&CommBuffer[0];
    CopyMem (&CommHeader->HeaderGuid, &gEdkiiMemoryProfileGuid, sizeof (gEdkiiMemoryProfileGuid));
    CommHeader->MessageLength = sizeof (SMRAM_PROFILE_PARAMETER_RECORDING_STATE);

    CommRecordingState                      = (SMRAM_PROFILE_PARAMETER_RECORDING_STATE *)&CommBuffer[OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)];
    CommRecordingState->Header.Command      = SMRAM_PROFILE_COMMAND_SET_RECORDING_STATE;
    CommRecordingState->Header.DataLength   = sizeof (*CommRecordingState);
    CommRecordingState->Header.ReturnStatus = (UINT64)-1;
    CommRecordingState->RecordingState      = MEMORY_PROFILE_RECORDING_ENABLE;

    CommSize = sizeof (EFI_GUID) + sizeof (UINTN) + CommHeader->MessageLength;
    SmmCommunication->Communicate (SmmCommunication, CommBuffer, &CommSize);
  }

  return Status;
}

/**
  The user Entry Point for Application. The user code starts with this function
  as the real entry point for the image goes into a library that calls this function.

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

  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.

**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  Status = GetUefiMemoryProfileData ();
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "GetUefiMemoryProfileData - %r\n", Status));
  }

  Status = GetSmramProfileData ();
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "GetSmramProfileData - %r\n", Status));
  }

  return EFI_SUCCESS;
}
