/** @file
A shell application to dump dynamic PCD settings.
Copyright (c) 2019, 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
//
// String token ID of help message text.
// Shell supports to find help message in the resource section of an application image if
// .MAN file is not found. This global variable is added to make build tool recognizes
// that the help string is consumed by user and then build tool will add the string into
// the resource section. Thus the application can use '-?' option to show help message in
// Shell.
//
GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStrDumpDynPcdHelpTokenId = STRING_TOKEN (STR_DUMP_DYN_PCD_HELP_INFORMATION);
#define MAJOR_VERSION 1
#define MINOR_VERSION 0
static EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation = NULL;
static EFI_PCD_PROTOCOL *mPiPcd = NULL;
static PCD_PROTOCOL *mPcd = NULL;
static EFI_GET_PCD_INFO_PROTOCOL *mPiPcdInfo = NULL;
static GET_PCD_INFO_PROTOCOL *mPcdInfo = NULL;
static CHAR16 *mTempPcdNameBuffer = NULL;
static UINTN mTempPcdNameBufferSize = 0;
static CONST CHAR8 mHex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
static UINTN Argc;
static CHAR16 **Argv;
/**
This function parse application ARG.
@return Status
**/
static
EFI_STATUS
GetArg (
VOID
)
{
EFI_STATUS Status;
EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters;
Status = gBS->HandleProtocol (
gImageHandle,
&gEfiShellParametersProtocolGuid,
(VOID **)&ShellParameters
);
if (EFI_ERROR (Status)) {
return Status;
}
Argc = ShellParameters->Argc;
Argv = ShellParameters->Argv;
return EFI_SUCCESS;
}
/**
Display current version.
**/
static
VOID
ShowVersion (
)
{
Print (L"DumpDynPcd Version %d.%02d\n", MAJOR_VERSION, MINOR_VERSION);
}
/**
Display Usage and Help information.
**/
static
VOID
ShowHelp (
)
{
Print (L"Dump dynamic[ex] PCD info.\n");
Print (L"\n");
Print (L"DumpDynPcd [PcdName]\n");
Print (L"\n");
Print (L" PcdName Specifies the name of PCD.\n");
Print (L" A literal[or partial] name or a pattern as specified in\n");
Print (L" the MetaiMatch() function of the EFI_UNICODE_COLLATION2_PROCOOL.\n");
Print (L" If it is absent, dump all PCDs' info.\n");
Print (L"The PCD data is printed as hexadecimal dump.\n");
}
/**
Dump some hexadecimal data to the screen.
@param[in] Indent How many spaces to indent the output.
@param[in] Offset The offset of the printing.
@param[in] DataSize The size in bytes of UserData.
@param[in] UserData The data to print out.
**/
static
VOID
DumpHex (
IN UINTN Indent,
IN UINTN Offset,
IN UINTN DataSize,
IN VOID *UserData
)
{
UINT8 *Data;
CHAR8 Val[50];
CHAR8 Str[20];
UINT8 TempByte;
UINTN Size;
UINTN Index;
Data = UserData;
while (DataSize != 0) {
Size = 16;
if (Size > DataSize) {
Size = DataSize;
}
for (Index = 0; Index < Size; Index += 1) {
TempByte = Data[Index];
Val[Index * 3 + 0] = mHex[TempByte >> 4];
Val[Index * 3 + 1] = mHex[TempByte & 0xF];
Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' ');
Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);
}
Val[Index * 3] = 0;
Str[Index] = 0;
Print (L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
Data += Size;
Offset += Size;
DataSize -= Size;
}
}
/**
Safely append with automatic string resizing given length of Destination and
desired length of copy from Source.
append the first D characters of Source to the end of Destination, where D is
the lesser of Count and the StrLen() of Source. If appending those D characters
will fit within Destination (whose Size is given as CurrentSize) and
still leave room for a NULL terminator, then those characters are appended,
starting at the original terminating NULL of Destination, and a new terminating
NULL is appended.
If appending D characters onto Destination will result in a overflow of the size
given in CurrentSize the string will be grown such that the copy can be performed
and CurrentSize will be updated to the new size.
If Source is NULL, there is nothing to append, just return the current buffer in
Destination.
if Destination is NULL, then ASSERT()
if Destination's current length (including NULL terminator) is already more then
CurrentSize, then ASSERT()
@param[in, out] Destination The String to append onto
@param[in, out] CurrentSize on call the number of bytes in Destination. On
return possibly the new size (still in bytes). if NULL
then allocate whatever is needed.
@param[in] Source The String to append from
@return Destination return the resultant string.
**/
static
CHAR16 *
InternalStrnCatGrow (
IN OUT CHAR16 **Destination,
IN OUT UINTN *CurrentSize,
IN CONST CHAR16 *Source
)
{
UINTN DestinationStartSize;
UINTN NewSize;
UINTN SourceLen;
SourceLen = StrLen (Source);
//
// ASSERTs
//
ASSERT (Destination != NULL);
//
// If there's nothing to do then just return Destination
//
if (Source == NULL) {
return (*Destination);
}
//
// allow for un-initialized pointers, based on size being 0
//
if ((CurrentSize != NULL) && (*CurrentSize == 0)) {
*Destination = NULL;
}
//
// allow for NULL pointers address as Destination
//
if (*Destination != NULL) {
ASSERT (CurrentSize != 0);
DestinationStartSize = StrSize (*Destination);
ASSERT (DestinationStartSize <= *CurrentSize);
} else {
DestinationStartSize = 0;
}
//
// Test and grow if required
//
if (CurrentSize != NULL) {
NewSize = *CurrentSize;
if (NewSize < DestinationStartSize + (SourceLen * sizeof (CHAR16))) {
while (NewSize < (DestinationStartSize + (SourceLen*sizeof (CHAR16)))) {
NewSize += 2 * SourceLen * sizeof (CHAR16);
}
*Destination = ReallocatePool (*CurrentSize, NewSize, *Destination);
*CurrentSize = NewSize;
}
} else {
NewSize = (SourceLen + 1)*sizeof (CHAR16);
*Destination = AllocateZeroPool (NewSize);
}
//
// Now use standard StrnCat on a big enough buffer
//
if (*Destination == NULL) {
return (NULL);
}
StrnCatS (*Destination, NewSize/sizeof (CHAR16), Source, SourceLen);
return *Destination;
}
/**
Get PCD type string based on input PCD type.
@param[in] TokenSpace PCD Token Space.
@param[in] PcdType The input PCD type.
@return Pointer to PCD type string.
**/
static
CHAR16 *
GetPcdTypeString (
IN CONST EFI_GUID *TokenSpace,
IN EFI_PCD_TYPE PcdType
)
{
UINTN BufLen;
CHAR16 *RetString;
BufLen = 0;
RetString = NULL;
switch (PcdType) {
case EFI_PCD_TYPE_8:
InternalStrnCatGrow (&RetString, &BufLen, L"UINT8");
break;
case EFI_PCD_TYPE_16:
InternalStrnCatGrow (&RetString, &BufLen, L"UINT16");
break;
case EFI_PCD_TYPE_32:
InternalStrnCatGrow (&RetString, &BufLen, L"UINT32");
break;
case EFI_PCD_TYPE_64:
InternalStrnCatGrow (&RetString, &BufLen, L"UINT64");
break;
case EFI_PCD_TYPE_BOOL:
InternalStrnCatGrow (&RetString, &BufLen, L"BOOLEAN");
break;
case EFI_PCD_TYPE_PTR:
InternalStrnCatGrow (&RetString, &BufLen, L"POINTER");
break;
default:
InternalStrnCatGrow (&RetString, &BufLen, L"UNKNOWN");
break;
}
if (TokenSpace == NULL) {
InternalStrnCatGrow (&RetString, &BufLen, L":DYNAMIC");
} else {
InternalStrnCatGrow (&RetString, &BufLen, L":DYNAMICEX");
}
return RetString;
}
/**
Dump PCD info.
@param[in] TokenSpace PCD Token Space.
@param[in] TokenNumber PCD Token Number.
@param[in] PcdInfo Pointer to PCD info.
**/
static
VOID
DumpPcdInfo (
IN CONST EFI_GUID *TokenSpace,
IN UINTN TokenNumber,
IN EFI_PCD_INFO *PcdInfo
)
{
CHAR16 *RetString;
UINT8 Uint8;
UINT16 Uint16;
UINT32 Uint32;
UINT64 Uint64;
BOOLEAN Boolean;
VOID *PcdData;
RetString = NULL;
if (PcdInfo->PcdName != NULL) {
Print (L"%a\n", PcdInfo->PcdName);
} else {
if (TokenSpace == NULL) {
Print (L"Default Token Space\n");
} else {
Print (L"%g\n", TokenSpace);
}
}
RetString = GetPcdTypeString (TokenSpace, PcdInfo->PcdType);
switch (PcdInfo->PcdType) {
case EFI_PCD_TYPE_8:
if (TokenSpace == NULL) {
Uint8 = mPcd->Get8 (TokenNumber);
} else {
Uint8 = mPiPcd->Get8 (TokenSpace, TokenNumber);
}
Print (L" Token = 0x%08x - Type = %H%-17s%N - Size = 0x%x - Value = 0x%x\n", TokenNumber, RetString, PcdInfo->PcdSize, Uint8);
break;
case EFI_PCD_TYPE_16:
if (TokenSpace == NULL) {
Uint16 = mPcd->Get16 (TokenNumber);
} else {
Uint16 = mPiPcd->Get16 (TokenSpace, TokenNumber);
}
Print (L" Token = 0x%08x - Type = %H%-17s%N - Size = 0x%x - Value = 0x%x\n", TokenNumber, RetString, PcdInfo->PcdSize, Uint16);
break;
case EFI_PCD_TYPE_32:
if (TokenSpace == NULL) {
Uint32 = mPcd->Get32 (TokenNumber);
} else {
Uint32 = mPiPcd->Get32 (TokenSpace, TokenNumber);
}
Print (L" Token = 0x%08x - Type = %H%-17s%N - Size = 0x%x - Value = 0x%x\n", TokenNumber, RetString, PcdInfo->PcdSize, Uint32);
break;
case EFI_PCD_TYPE_64:
if (TokenSpace == NULL) {
Uint64 = mPcd->Get64 (TokenNumber);
} else {
Uint64 = mPiPcd->Get64 (TokenSpace, TokenNumber);
}
Print (L" Token = 0x%08x - Type = %H%-17s%N - Size = 0x%x - Value = 0x%lx\n", TokenNumber, RetString, PcdInfo->PcdSize, Uint64);
break;
case EFI_PCD_TYPE_BOOL:
if (TokenSpace == NULL) {
Boolean = mPcd->GetBool (TokenNumber);
} else {
Boolean = mPiPcd->GetBool (TokenSpace, TokenNumber);
}
Print (L" Token = 0x%08x - Type = %H%-17s%N - Size = 0x%x - Value = %a\n", TokenNumber, RetString, PcdInfo->PcdSize, Boolean ? "TRUE" : "FALSE");
break;
case EFI_PCD_TYPE_PTR:
if (TokenSpace == NULL) {
PcdData = mPcd->GetPtr (TokenNumber);
} else {
PcdData = mPiPcd->GetPtr (TokenSpace, TokenNumber);
}
Print (L" Token = 0x%08x - Type = %H%-17s%N - Size = 0x%x\n", TokenNumber, RetString, PcdInfo->PcdSize);
DumpHex (2, 0, PcdInfo->PcdSize, PcdData);
break;
default:
return;
}
if (RetString != NULL) {
FreePool (RetString);
}
Print (L"\n");
}
/**
Show one or all PCDs' info.
@param[in] InputPcdName Pointer to PCD name to show. If NULL, show all PCDs' info.
@retval EFI_SUCCESS Command completed successfully.
@retval EFI_OUT_OF_RESOURCES Not enough resources were available to run the command.
@retval EFI_ABORTED Aborted by user.
@retval EFI_NOT_FOUND The specified PCD is not found.
**/
static
EFI_STATUS
ProcessPcd (
IN CHAR16 *InputPcdName
)
{
EFI_STATUS Status;
EFI_GUID *TokenSpace;
UINTN TokenNumber;
EFI_PCD_INFO PcdInfo;
BOOLEAN Found;
UINTN PcdNameSize;
PcdInfo.PcdName = NULL;
PcdInfo.PcdSize = 0;
PcdInfo.PcdType = 0xFF;
Found = FALSE;
Print (L"Current system SKU ID: 0x%x\n\n", mPiPcdInfo->GetSku ());
TokenSpace = NULL;
do {
TokenNumber = 0;
do {
Status = mPiPcd->GetNextToken (TokenSpace, &TokenNumber);
if (!EFI_ERROR (Status) && (TokenNumber != 0)) {
if (TokenSpace == NULL) {
//
// PCD in default Token Space.
//
mPcdInfo->GetInfo (TokenNumber, &PcdInfo);
} else {
mPiPcdInfo->GetInfo (TokenSpace, TokenNumber, &PcdInfo);
}
if (InputPcdName != NULL) {
if (PcdInfo.PcdName == NULL) {
continue;
}
PcdNameSize = AsciiStrSize (PcdInfo.PcdName) * sizeof (CHAR16);
if (mTempPcdNameBuffer == NULL) {
mTempPcdNameBufferSize = PcdNameSize;
mTempPcdNameBuffer = AllocatePool (mTempPcdNameBufferSize);
} else if (mTempPcdNameBufferSize < PcdNameSize) {
mTempPcdNameBuffer = ReallocatePool (mTempPcdNameBufferSize, PcdNameSize, mTempPcdNameBuffer);
mTempPcdNameBufferSize = PcdNameSize;
}
if (mTempPcdNameBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
AsciiStrToUnicodeStrS (PcdInfo.PcdName, mTempPcdNameBuffer, mTempPcdNameBufferSize / sizeof (CHAR16));
//
// Compare the input PCD name with the PCD name in PCD database.
//
if ((StrStr (mTempPcdNameBuffer, InputPcdName) != NULL) ||
((mUnicodeCollation != NULL) && mUnicodeCollation->MetaiMatch (mUnicodeCollation, mTempPcdNameBuffer, InputPcdName)))
{
//
// Found matched PCD.
//
DumpPcdInfo (TokenSpace, TokenNumber, &PcdInfo);
Found = TRUE;
}
} else {
DumpPcdInfo (TokenSpace, TokenNumber, &PcdInfo);
}
}
} while (!EFI_ERROR (Status) && TokenNumber != 0);
Status = mPiPcd->GetNextTokenSpace ((CONST EFI_GUID **)&TokenSpace);
} while (!EFI_ERROR (Status) && TokenSpace != NULL);
if ((InputPcdName != NULL) && !Found) {
//
// The specified PCD is not found, print error.
//
Print (L"%EError. %NNo matching PCD found: %s.\n", InputPcdName);
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
/**
Main entrypoint for DumpDynPcd shell application.
@param[in] ImageHandle The image handle.
@param[in] SystemTable The system table.
@retval EFI_SUCCESS Command completed successfully.
@retval EFI_INVALID_PARAMETER Command usage error.
@retval EFI_OUT_OF_RESOURCES Not enough resources were available to run the command.
@retval EFI_ABORTED Aborted by user.
@retval EFI_NOT_FOUND The specified PCD is not found.
@retval Others Error status returned from gBS->LocateProtocol.
**/
EFI_STATUS
EFIAPI
DumpDynPcdMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
CHAR16 *InputPcdName;
InputPcdName = NULL;
Status = gBS->LocateProtocol (&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID **)&mUnicodeCollation);
if (EFI_ERROR (Status)) {
mUnicodeCollation = NULL;
}
Status = gBS->LocateProtocol (&gEfiPcdProtocolGuid, NULL, (VOID **)&mPiPcd);
if (EFI_ERROR (Status)) {
Print (L"DumpDynPcd: %EError. %NPI PCD protocol is not present.\n");
return Status;
}
Status = gBS->LocateProtocol (&gEfiGetPcdInfoProtocolGuid, NULL, (VOID **)&mPiPcdInfo);
if (EFI_ERROR (Status)) {
Print (L"DumpDynPcd: %EError. %NPI PCD info protocol is not present.\n");
return Status;
}
Status = gBS->LocateProtocol (&gPcdProtocolGuid, NULL, (VOID **)&mPcd);
if (EFI_ERROR (Status)) {
Print (L"DumpDynPcd: %EError. %NPCD protocol is not present.\n");
return Status;
}
Status = gBS->LocateProtocol (&gGetPcdInfoProtocolGuid, NULL, (VOID **)&mPcdInfo);
if (EFI_ERROR (Status)) {
Print (L"DumpDynPcd: %EError. %NPCD info protocol is not present.\n");
return Status;
}
//
// get the command line arguments
//
Status = GetArg ();
if (EFI_ERROR (Status)) {
Print (L"DumpDynPcd: %EError. %NThe input parameters are not recognized.\n");
Status = EFI_INVALID_PARAMETER;
return Status;
}
if (Argc > 2) {
Print (L"DumpDynPcd: %EError. %NToo many arguments specified.\n");
Status = EFI_INVALID_PARAMETER;
return Status;
}
if (Argc == 1) {
Status = ProcessPcd (InputPcdName);
goto Done;
}
if ((StrCmp (Argv[1], L"-?") == 0) || (StrCmp (Argv[1], L"-h") == 0) || (StrCmp (Argv[1], L"-H") == 0)) {
ShowHelp ();
goto Done;
} else {
if ((StrCmp (Argv[1], L"-v") == 0) || (StrCmp (Argv[1], L"-V") == 0)) {
ShowVersion ();
goto Done;
} else {
if (StrStr (Argv[1], L"-") != NULL) {
Print (L"DumpDynPcd: %EError. %NThe argument '%B%s%N' is invalid.\n", Argv[1]);
goto Done;
}
}
}
InputPcdName = Argv[1];
Status = ProcessPcd (InputPcdName);
Done:
if (mTempPcdNameBuffer != NULL) {
FreePool (mTempPcdNameBuffer);
}
return Status;
}