/** @file
Shell application to dump SMI handler profile information.
Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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" 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" %a\n", PdbString);
}
Print (L" \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" HandlerType)) {
Print (L" HandlerType=\"%g\"", &SmiStruct->HandlerType);
}
Print (L">\n");
for (Index = 0; Index < SmiStruct->HandlerCount; Index++) {
Print (L" ContextBufferSize != 0) {
DumpSmiChildContext (&SmiStruct->HandlerType, (UINT8 *)SmiHandlerStruct + SmiHandlerStruct->ContextBufferOffset, SmiHandlerStruct->ContextBufferSize);
}
Print (L">\n");
ImageStruct = GetImageFromRef ((UINTN)SmiHandlerStruct->ImageRef);
NameString = GetDriverNameString (ImageStruct);
Print (L" \n", SmiHandlerStruct->ImageRef, NameString);
if ((ImageStruct != NULL) && (ImageStruct->PdbStringOffset != 0)) {
Print (L" %a\n", (UINT8 *)ImageStruct + ImageStruct->PdbStringOffset);
}
Print (L" \n");
Print (L" \n", SmiHandlerStruct->Handler);
if (ImageStruct != NULL) {
Print (L" 0x%x\n", (UINTN)(SmiHandlerStruct->Handler - ImageStruct->ImageBase));
}
Print (L" \n", SmiHandlerStruct->Handler);
Print (L" \n", SmiHandlerStruct->CallerAddr);
if (ImageStruct != NULL) {
Print (L" 0x%x\n", (UINTN)(SmiHandlerStruct->CallerAddr - ImageStruct->ImageBase));
}
Print (L" \n", SmiHandlerStruct->Handler);
SmiHandlerStruct = (VOID *)((UINTN)SmiHandlerStruct + SmiHandlerStruct->Length);
Print (L" \n");
}
Print (L" \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"\n");
Print (L"\n");
Print (L"\n");
Print (L" \n");
DumpSmmLoadedImage ();
Print (L"\n\n");
//
// Dump SMI Handler
//
Print (L"\n");
Print (L" \n\n");
Print (L" \n");
Print (L" \n");
DumpSmiHandler (SmmCoreSmiHandlerCategoryRootHandler);
Print (L" \n\n");
Print (L" \n");
Print (L" \n");
DumpSmiHandler (SmmCoreSmiHandlerCategoryGuidHandler);
Print (L" \n\n");
Print (L" \n");
Print (L" \n");
DumpSmiHandler (SmmCoreSmiHandlerCategoryHardwareHandler);
Print (L" \n\n");
Print (L"\n");
Print (L"\n");
if (mSmiHandlerProfileDatabase != NULL) {
FreePool (mSmiHandlerProfileDatabase);
}
return EFI_SUCCESS;
}