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

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

**/

#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/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;
}
