| /** @file | |
| A shell application to dump dynamic PCD settings. | |
| Copyright (c) 2019, 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/DebugLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <Protocol/UnicodeCollation.h> | |
| #include <Protocol/PiPcd.h> | |
| #include <Protocol/Pcd.h> | |
| #include <Protocol/PiPcdInfo.h> | |
| #include <Protocol/PcdInfo.h> | |
| #include <Protocol/ShellParameters.h> | |
| #include <Protocol/Shell.h> | |
| // | |
| // 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 = %-17s - 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 = %-17s - 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 = %-17s - 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 = %-17s - 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 = %-17s - 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 = %-17s - 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"Error. No 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: Error. PI PCD protocol is not present.\n"); | |
| return Status; | |
| } | |
| Status = gBS->LocateProtocol (&gEfiGetPcdInfoProtocolGuid, NULL, (VOID **)&mPiPcdInfo); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"DumpDynPcd: Error. PI PCD info protocol is not present.\n"); | |
| return Status; | |
| } | |
| Status = gBS->LocateProtocol (&gPcdProtocolGuid, NULL, (VOID **)&mPcd); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"DumpDynPcd: Error. PCD protocol is not present.\n"); | |
| return Status; | |
| } | |
| Status = gBS->LocateProtocol (&gGetPcdInfoProtocolGuid, NULL, (VOID **)&mPcdInfo); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"DumpDynPcd: Error. PCD info protocol is not present.\n"); | |
| return Status; | |
| } | |
| // | |
| // get the command line arguments | |
| // | |
| Status = GetArg (); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"DumpDynPcd: Error. The input parameters are not recognized.\n"); | |
| Status = EFI_INVALID_PARAMETER; | |
| return Status; | |
| } | |
| if (Argc > 2) { | |
| Print (L"DumpDynPcd: Error. Too 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: Error. The argument '%s' is invalid.\n", Argv[1]); | |
| goto Done; | |
| } | |
| } | |
| } | |
| InputPcdName = Argv[1]; | |
| Status = ProcessPcd (InputPcdName); | |
| Done: | |
| if (mTempPcdNameBuffer != NULL) { | |
| FreePool (mTempPcdNameBuffer); | |
| } | |
| return Status; | |
| } |