/** @file
  Shell application to dump SMI handler profile information.

Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include <PiDxe.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiLib.h>
#include <Library/DevicePathLib.h>
#include <Library/PeCoffGetEntryPointLib.h>
#include <Library/DxeServicesLib.h>
#include <Protocol/SmmCommunication.h>
#include <Guid/PiSmmCommunicationRegionTable.h>

#include <Guid/SmiHandlerProfile.h>

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

VOID   *mSmiHandlerProfileDatabase;
UINTN  mSmiHandlerProfileDatabaseSize;

/**
  This function dump raw data.

  @param  Data  raw data
  @param  Size  raw data size
**/
VOID
InternalDumpData (
  IN UINT8  *Data,
  IN UINTN  Size
  )
{
  UINTN  Index;
  for (Index = 0; Index < Size; Index++) {
    Print (L"%02x", (UINTN)Data[Index]);
    if ((Index + 1) != Size) {
      Print (L" ");
    }
  }
}

/**
  Get SMI handler profile database.
**/
VOID
GetSmiHandlerProfileDatabase(
  VOID
  )
{
  EFI_STATUS                                          Status;
  UINTN                                               CommSize;
  UINT8                                               *CommBuffer;
  EFI_SMM_COMMUNICATE_HEADER                          *CommHeader;
  SMI_HANDLER_PROFILE_PARAMETER_GET_INFO              *CommGetInfo;
  SMI_HANDLER_PROFILE_PARAMETER_GET_DATA_BY_OFFSET    *CommGetData;
  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;

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

  MinimalSizeNeeded = EFI_PAGE_SIZE;

  Status = EfiGetSystemConfigurationTable(
             &gEdkiiPiSmmCommunicationRegionTableGuid,
             (VOID **)&PiSmmCommunicationRegionTable
             );
  if (EFI_ERROR(Status)) {
    Print(L"SmiHandlerProfile: Get PiSmmCommunicationRegionTable - %r\n", Status);
    return ;
  }
  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;

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

  CommGetInfo = (SMI_HANDLER_PROFILE_PARAMETER_GET_INFO *)&CommBuffer[OFFSET_OF(EFI_SMM_COMMUNICATE_HEADER, Data)];
  CommGetInfo->Header.Command = SMI_HANDLER_PROFILE_COMMAND_GET_INFO;
  CommGetInfo->Header.DataLength = sizeof(*CommGetInfo);
  CommGetInfo->Header.ReturnStatus = (UINT64)-1;
  CommGetInfo->DataSize = 0;

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

  if (CommGetInfo->Header.ReturnStatus != 0) {
    Print(L"SmiHandlerProfile: GetInfo - 0x%0x\n", CommGetInfo->Header.ReturnStatus);
    return ;
  }

  mSmiHandlerProfileDatabaseSize = (UINTN)CommGetInfo->DataSize;

  //
  // Get Data
  //
  mSmiHandlerProfileDatabase = AllocateZeroPool(mSmiHandlerProfileDatabaseSize);
  if (mSmiHandlerProfileDatabase == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    Print(L"SmiHandlerProfile: AllocateZeroPool (0x%x) for dump buffer - %r\n", mSmiHandlerProfileDatabaseSize, Status);
    return ;
  }

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

  CommGetData = (SMI_HANDLER_PROFILE_PARAMETER_GET_DATA_BY_OFFSET *)&CommBuffer[OFFSET_OF(EFI_SMM_COMMUNICATE_HEADER, Data)];
  CommGetData->Header.Command = SMI_HANDLER_PROFILE_COMMAND_GET_DATA_BY_OFFSET;
  CommGetData->Header.DataLength = sizeof(*CommGetData);
  CommGetData->Header.ReturnStatus = (UINT64)-1;

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

  CommGetData->DataBuffer = (PHYSICAL_ADDRESS)(UINTN)Buffer;
  CommGetData->DataOffset = 0;
  while (CommGetData->DataOffset < mSmiHandlerProfileDatabaseSize) {
    Offset = (UINTN)CommGetData->DataOffset;
    if (Size <= (mSmiHandlerProfileDatabaseSize - CommGetData->DataOffset)) {
      CommGetData->DataSize = (UINT64)Size;
    } else {
      CommGetData->DataSize = (UINT64)(mSmiHandlerProfileDatabaseSize - CommGetData->DataOffset);
    }
    Status = SmmCommunication->Communicate(SmmCommunication, CommBuffer, &CommSize);
    ASSERT_EFI_ERROR(Status);

    if (CommGetData->Header.ReturnStatus != 0) {
      FreePool(mSmiHandlerProfileDatabase);
      mSmiHandlerProfileDatabase = NULL;
      Print(L"SmiHandlerProfile: GetData - 0x%x\n", CommGetData->Header.ReturnStatus);
      return ;
    }
    CopyMem((UINT8 *)mSmiHandlerProfileDatabase + Offset, (VOID *)(UINTN)CommGetData->DataBuffer, (UINTN)CommGetData->DataSize);
  }

  DEBUG ((DEBUG_INFO, "SmiHandlerProfileSize - 0x%x\n", mSmiHandlerProfileDatabaseSize));

  return ;
}

/**
  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] ImageStruct  Point to the image structure.

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

**/
CHAR8 *
GetDriverNameString (
  IN SMM_CORE_IMAGE_DATABASE_STRUCTURE  *ImageStruct
  )
{
  EFI_STATUS                  Status;
  CHAR16                      *NameString;
  UINTN                       StringSize;

  if (ImageStruct == NULL) {
    return "???";
  }

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

  if (!IsZeroGuid (&ImageStruct->FileGuid)) {
    //
    // Try to get the image's FFS UI section by image GUID
    //
    NameString = NULL;
    StringSize = 0;
    Status = GetSectionFromAnyFv (
              &ImageStruct->FileGuid,
              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", &ImageStruct->FileGuid);
  return mNameString;
}

/**
  Get image structure from reference index.

  @param ImageRef   the image reference index

  @return image structure
**/
SMM_CORE_IMAGE_DATABASE_STRUCTURE *
GetImageFromRef (
  IN UINTN ImageRef
  )
{
  SMM_CORE_IMAGE_DATABASE_STRUCTURE  *ImageStruct;

  ImageStruct = (VOID *)mSmiHandlerProfileDatabase;
  while ((UINTN)ImageStruct < (UINTN)mSmiHandlerProfileDatabase + mSmiHandlerProfileDatabaseSize) {
    if (ImageStruct->Header.Signature == SMM_CORE_IMAGE_DATABASE_SIGNATURE) {
      if (ImageStruct->ImageRef == ImageRef) {
        return ImageStruct;
      }
    }
    ImageStruct = (VOID *)((UINTN)ImageStruct + ImageStruct->Header.Length);
  }

  return NULL;
}

/**
  Dump SMM loaded image information.
**/
VOID
DumpSmmLoadedImage(
  VOID
  )
{
  SMM_CORE_IMAGE_DATABASE_STRUCTURE  *ImageStruct;
  CHAR8                              *PdbString;
  CHAR8                              *NameString;

  ImageStruct = (VOID *)mSmiHandlerProfileDatabase;
  while ((UINTN)ImageStruct < (UINTN)mSmiHandlerProfileDatabase + mSmiHandlerProfileDatabaseSize) {
    if (ImageStruct->Header.Signature == SMM_CORE_IMAGE_DATABASE_SIGNATURE) {
      NameString = GetDriverNameString (ImageStruct);
      Print(L"  <Image Name=\"%a\"", NameString);
      Print(L" Base=\"0x%lx\" Size=\"0x%lx\"", ImageStruct->ImageBase, ImageStruct->ImageSize);
      if (ImageStruct->EntryPoint != 0) {
        Print(L" EntryPoint=\"0x%lx\"", ImageStruct->EntryPoint);
      }
      Print(L" FvFile=\"%g\"", &ImageStruct->FileGuid);
      Print(L" RefId=\"0x%x\"", ImageStruct->ImageRef);
      Print(L">\n");
      if (ImageStruct->PdbStringOffset != 0) {
        PdbString = (CHAR8 *)((UINTN)ImageStruct + ImageStruct->PdbStringOffset);
        Print(L"    <Pdb>%a</Pdb>\n", PdbString);
      }
      Print(L"  </Image>\n");
    }

    ImageStruct = (VOID *)((UINTN)ImageStruct + ImageStruct->Header.Length);
  }

  return;
}

CHAR8 *mSxTypeString[] = {
  "SxS0",
  "SxS1",
  "SxS2",
  "SxS3",
  "SxS4",
  "SxS5",
};

/**
  Convert SxType to a string.

  @param Type SxType

  @return SxType string
**/
CHAR8 *
SxTypeToString (
  IN EFI_SLEEP_TYPE  Type
  )
{
  if (Type >= 0 && Type <= ARRAY_SIZE(mSxTypeString)) {
    return mSxTypeString[Type];
  } else {
    AsciiSPrint (mNameString, sizeof(mNameString), "0x%x", Type);
    return mNameString;
  }
}

CHAR8 *mSxPhaseString[] = {
  "SxEntry",
  "SxExit",
};

/**
  Convert SxPhase to a string.

  @param Phase SxPhase

  @return SxPhase string
**/
CHAR8 *
SxPhaseToString (
  IN EFI_SLEEP_PHASE Phase
  )
{
  if (Phase >= 0 && Phase <= ARRAY_SIZE(mSxPhaseString)) {
    return mSxPhaseString[Phase];
  } else {
    AsciiSPrint (mNameString, sizeof(mNameString), "0x%x", Phase);
    return mNameString;
  }
}

CHAR8 *mPowerButtonPhaseString[] = {
  "PowerButtonEntry",
  "PowerButtonExit",
};

/**
  Convert PowerButtonPhase to a string.

  @param Phase PowerButtonPhase

  @return PowerButtonPhase string
**/
CHAR8 *
PowerButtonPhaseToString (
  IN EFI_POWER_BUTTON_PHASE  Phase
  )
{
  if (Phase >= 0 && Phase <= ARRAY_SIZE(mPowerButtonPhaseString)) {
    return mPowerButtonPhaseString[Phase];
  } else {
    AsciiSPrint (mNameString, sizeof(mNameString), "0x%x", Phase);
    return mNameString;
  }
}

CHAR8 *mStandbyButtonPhaseString[] = {
  "StandbyButtonEntry",
  "StandbyButtonExit",
};

/**
  Convert StandbyButtonPhase to a string.

  @param Phase StandbyButtonPhase

  @return StandbyButtonPhase string
**/
CHAR8 *
StandbyButtonPhaseToString (
  IN EFI_STANDBY_BUTTON_PHASE  Phase
  )
{
  if (Phase >= 0 && Phase <= ARRAY_SIZE(mStandbyButtonPhaseString)) {
    return mStandbyButtonPhaseString[Phase];
  } else {
    AsciiSPrint (mNameString, sizeof(mNameString), "0x%x", Phase);
    return mNameString;
  }
}

CHAR8 *mIoTrapTypeString[] = {
  "WriteTrap",
  "ReadTrap",
  "ReadWriteTrap",
};

/**
  Convert IoTrapType to a string.

  @param Type IoTrapType

  @return IoTrapType string
**/
CHAR8 *
IoTrapTypeToString (
  IN EFI_SMM_IO_TRAP_DISPATCH_TYPE  Type
  )
{
  if (Type >= 0 && Type <= ARRAY_SIZE(mIoTrapTypeString)) {
    return mIoTrapTypeString[Type];
  } else {
    AsciiSPrint (mNameString, sizeof(mNameString), "0x%x", Type);
    return mNameString;
  }
}

CHAR8 *mUsbTypeString[] = {
  "UsbLegacy",
  "UsbWake",
};

/**
  Convert UsbType to a string.

  @param Type UsbType

  @return UsbType string
**/
CHAR8 *
UsbTypeToString (
  IN EFI_USB_SMI_TYPE          Type
  )
{
  if (Type >= 0 && Type <= ARRAY_SIZE(mUsbTypeString)) {
    return mUsbTypeString[Type];
  } else {
    AsciiSPrint (mNameString, sizeof(mNameString), "0x%x", Type);
    return mNameString;
  }
}

/**
  Dump SMI child context.

  @param HandlerType  the handler type
  @param Context      the handler context
  @param ContextSize  the handler context size
**/
VOID
DumpSmiChildContext (
  IN EFI_GUID   *HandlerType,
  IN VOID       *Context,
  IN UINTN      ContextSize
  )
{
  CHAR16        *Str;

  if (CompareGuid (HandlerType, &gEfiSmmSwDispatch2ProtocolGuid)) {
    Print(L" SwSmi=\"0x%lx\"", ((SMI_HANDLER_PROFILE_SW_REGISTER_CONTEXT *)Context)->SwSmiInputValue);
  } else if (CompareGuid (HandlerType, &gEfiSmmSxDispatch2ProtocolGuid)) {
    Print(L" SxType=\"%a\"", SxTypeToString(((EFI_SMM_SX_REGISTER_CONTEXT *)Context)->Type));
    Print(L" SxPhase=\"%a\"", SxPhaseToString(((EFI_SMM_SX_REGISTER_CONTEXT *)Context)->Phase));
  } else if (CompareGuid (HandlerType, &gEfiSmmPowerButtonDispatch2ProtocolGuid)) {
    Print(L" PowerButtonPhase=\"%a\"", PowerButtonPhaseToString(((EFI_SMM_POWER_BUTTON_REGISTER_CONTEXT *)Context)->Phase));
  } else if (CompareGuid (HandlerType, &gEfiSmmStandbyButtonDispatch2ProtocolGuid)) {
    Print(L" StandbyButtonPhase=\"%a\"", StandbyButtonPhaseToString(((EFI_SMM_STANDBY_BUTTON_REGISTER_CONTEXT *)Context)->Phase));
  } else if (CompareGuid (HandlerType, &gEfiSmmPeriodicTimerDispatch2ProtocolGuid)) {
    Print(L" PeriodicTimerPeriod=\"%ld\"", ((EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT *)Context)->Period);
    Print(L" PeriodicTimerSmiTickInterval=\"%ld\"", ((EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT *)Context)->SmiTickInterval);
  } else if (CompareGuid (HandlerType, &gEfiSmmGpiDispatch2ProtocolGuid)) {
    Print(L" GpiNum=\"0x%lx\"", ((EFI_SMM_GPI_REGISTER_CONTEXT *)Context)->GpiNum);
  } else if (CompareGuid (HandlerType, &gEfiSmmIoTrapDispatch2ProtocolGuid)) {
    Print(L" IoTrapAddress=\"0x%x\"", ((EFI_SMM_IO_TRAP_REGISTER_CONTEXT *)Context)->Address);
    Print(L" IoTrapLength=\"0x%x\"", ((EFI_SMM_IO_TRAP_REGISTER_CONTEXT *)Context)->Length);
    Print(L" IoTrapType=\"%a\"", IoTrapTypeToString(((EFI_SMM_IO_TRAP_REGISTER_CONTEXT *)Context)->Type));
  } else if (CompareGuid (HandlerType, &gEfiSmmUsbDispatch2ProtocolGuid)) {
    Print(L" UsbType=\"0x%x\"", UsbTypeToString(((SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT *)Context)->Type));
    Str = ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL *)(((SMI_HANDLER_PROFILE_USB_REGISTER_CONTEXT *)Context) + 1), TRUE, TRUE);
    Print(L" UsbDevicePath=\"%s\"", Str);
    if (Str != NULL) {
      FreePool (Str);
    }
  } else {
    Print(L" Context=\"");
    InternalDumpData (Context, ContextSize);
    Print(L"\"");
  }
}

/**
  Dump SMI handler in HandlerCategory.

  @param HandlerCategory  SMI handler category
**/
VOID
DumpSmiHandler(
  IN UINT32 HandlerCategory
  )
{
  SMM_CORE_SMI_DATABASE_STRUCTURE    *SmiStruct;
  SMM_CORE_SMI_HANDLER_STRUCTURE     *SmiHandlerStruct;
  UINTN                              Index;
  SMM_CORE_IMAGE_DATABASE_STRUCTURE  *ImageStruct;
  CHAR8                              *NameString;

  SmiStruct = (VOID *)mSmiHandlerProfileDatabase;
  while ((UINTN)SmiStruct < (UINTN)mSmiHandlerProfileDatabase + mSmiHandlerProfileDatabaseSize) {
    if ((SmiStruct->Header.Signature == SMM_CORE_SMI_DATABASE_SIGNATURE) && (SmiStruct->HandlerCategory == HandlerCategory)) {
      SmiHandlerStruct = (VOID *)(SmiStruct + 1);
      Print(L"  <SmiEntry");
      if (!IsZeroGuid (&SmiStruct->HandlerType)) {
        Print(L" HandlerType=\"%g\"", &SmiStruct->HandlerType);
      }
      Print(L">\n");
      for (Index = 0; Index < SmiStruct->HandlerCount; Index++) {
        Print(L"    <SmiHandler");
        if (SmiHandlerStruct->ContextBufferSize != 0) {
          DumpSmiChildContext (&SmiStruct->HandlerType, (UINT8 *)SmiHandlerStruct + SmiHandlerStruct->ContextBufferOffset, SmiHandlerStruct->ContextBufferSize);
        }
        Print(L">\n");
        ImageStruct = GetImageFromRef((UINTN)SmiHandlerStruct->ImageRef);
        NameString = GetDriverNameString (ImageStruct);
        Print(L"      <Module RefId=\"0x%x\" Name=\"%a\">\n", SmiHandlerStruct->ImageRef, NameString);
        if ((ImageStruct != NULL) && (ImageStruct->PdbStringOffset != 0)) {
          Print(L"      <Pdb>%a</Pdb>\n", (UINT8 *)ImageStruct + ImageStruct->PdbStringOffset);
        }
        Print(L"      </Module>\n");
        Print(L"      <Handler Address=\"0x%lx\">\n", SmiHandlerStruct->Handler);
        if (ImageStruct != NULL) {
          Print(L"         <RVA>0x%x</RVA>\n", (UINTN) (SmiHandlerStruct->Handler - ImageStruct->ImageBase));
        }
        Print(L"      </Handler>\n", SmiHandlerStruct->Handler);
        Print(L"      <Caller Address=\"0x%lx\">\n", SmiHandlerStruct->CallerAddr);
        if (ImageStruct != NULL) {
          Print(L"         <RVA>0x%x</RVA>\n", (UINTN) (SmiHandlerStruct->CallerAddr - ImageStruct->ImageBase));
        }
        Print(L"      </Caller>\n", SmiHandlerStruct->Handler);
        SmiHandlerStruct = (VOID *)((UINTN)SmiHandlerStruct + SmiHandlerStruct->Length);
        Print(L"    </SmiHandler>\n");
      }
      Print(L"  </SmiEntry>\n");
    }
    SmiStruct = (VOID *)((UINTN)SmiStruct + SmiStruct->Header.Length);
  }

  return;
}

/**
  The Entry Point for SMI handler profile info application.

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

  @retval EFI_SUCCESS    The entry point is executed successfully.
  @retval Other          Some error occurred when executing this entry point.
**/
EFI_STATUS
EFIAPI
SmiHandlerProfileInfoEntrypoint (
  IN EFI_HANDLE           ImageHandle,
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
{
  GetSmiHandlerProfileDatabase();

  if (mSmiHandlerProfileDatabase == NULL) {
    return EFI_SUCCESS;
  }

  //
  // Dump all image
  //
  Print(L"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
  Print(L"<SmiHandlerProfile>\n");
  Print(L"<ImageDatabase>\n");
  Print(L"  <!-- SMM image loaded -->\n");
  DumpSmmLoadedImage();
  Print(L"</ImageDatabase>\n\n");

  //
  // Dump SMI Handler
  //
  Print(L"<SmiHandlerDatabase>\n");
  Print(L"  <!-- SMI Handler registered -->\n\n");
  Print(L"  <SmiHandlerCategory Name=\"RootSmi\">\n");
  Print(L"  <!-- The root SMI Handler registered by SmmCore -->\n");
  DumpSmiHandler(SmmCoreSmiHandlerCategoryRootHandler);
  Print(L"  </SmiHandlerCategory>\n\n");

  Print(L"  <SmiHandlerCategory Name=\"GuidSmi\">\n");
  Print(L"  <!-- The GUID SMI Handler registered by SmmCore -->\n");
  DumpSmiHandler(SmmCoreSmiHandlerCategoryGuidHandler);
  Print(L"  </SmiHandlerCategory>\n\n");

  Print(L"  <SmiHandlerCategory Name=\"HardwareSmi\">\n");
  Print(L"  <!-- The hardware SMI Handler registered by SmmChildDispatcher -->\n");
  DumpSmiHandler(SmmCoreSmiHandlerCategoryHardwareHandler);
  Print(L"  </SmiHandlerCategory>\n\n");

  Print(L"</SmiHandlerDatabase>\n");
  Print(L"</SmiHandlerProfile>\n");

  if (mSmiHandlerProfileDatabase != NULL) {
    FreePool(mSmiHandlerProfileDatabase);
  }

  return EFI_SUCCESS;
}
