]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
ShellPkg: elevate DumpHex() from Debug1-internal to generic-internal
[mirror_edk2.git] / ShellPkg / Library / UefiShellCommandLib / UefiShellCommandLib.c
index 18ae9f33844c5dcfa81141624f93c85875aae6e1..92a88d18a065b95b98631e91061418792fe13c0c 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Provides interface to shell internal functions for shell commands.\r
 \r
-  (C) Copyright 2013-2014, Hewlett-Packard Development Company, L.P.\r
+  (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>\r
   Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
@@ -29,6 +29,25 @@ STATIC UINTN                              mFsMaxCount = 0;
 STATIC UINTN                              mBlkMaxCount = 0;\r
 STATIC BUFFER_LIST                        mFileHandleList;\r
 \r
+STATIC CONST CHAR8 Hex[] = {\r
+  '0',\r
+  '1',\r
+  '2',\r
+  '3',\r
+  '4',\r
+  '5',\r
+  '6',\r
+  '7',\r
+  '8',\r
+  '9',\r
+  'A',\r
+  'B',\r
+  'C',\r
+  'D',\r
+  'E',\r
+  'F'\r
+};\r
+\r
 // global variables required by library class.\r
 EFI_UNICODE_COLLATION_PROTOCOL    *gUnicodeCollation            = NULL;\r
 SHELL_MAP_LIST                    gShellMapList;\r
@@ -215,14 +234,80 @@ ShellCommandLibDestructor (
 }\r
 \r
 /**\r
-  Checks if a command is already on the list.\r
+  Find a dynamic command protocol instance given a command name string.\r
+\r
+  @param CommandString  the command name string\r
+\r
+  @return instance      the command protocol instance, if dynamic command instance found\r
+  @retval NULL          no dynamic command protocol instance found for name\r
+**/\r
+CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *\r
+EFIAPI\r
+ShellCommandFindDynamicCommand (\r
+  IN CONST CHAR16 *CommandString\r
+  )\r
+{\r
+  EFI_STATUS                          Status;\r
+  EFI_HANDLE                          *CommandHandleList;\r
+  EFI_HANDLE                          *NextCommand;\r
+  EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;\r
+\r
+  CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);\r
+  if (CommandHandleList == NULL) {\r
+    //\r
+    // not found or out of resources\r
+    //\r
+    return NULL; \r
+  }\r
+\r
+  for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {\r
+    Status = gBS->HandleProtocol(\r
+      *NextCommand,\r
+      &gEfiShellDynamicCommandProtocolGuid,\r
+      (VOID **)&DynamicCommand\r
+      );\r
+\r
+    if (EFI_ERROR(Status)) {\r
+      continue;\r
+    }\r
+\r
+    if (gUnicodeCollation->StriColl(\r
+          gUnicodeCollation,\r
+          (CHAR16*)CommandString,\r
+          (CHAR16*)DynamicCommand->CommandName) == 0 \r
+          ){\r
+        FreePool(CommandHandleList);\r
+        return (DynamicCommand);\r
+    }\r
+  }\r
+\r
+  FreePool(CommandHandleList);\r
+  return (NULL);\r
+}\r
+\r
+/**\r
+  Checks if a command exists as a dynamic command protocol instance\r
 \r
   @param[in] CommandString        The command string to check for on the list.\r
 **/\r
 BOOLEAN\r
 EFIAPI\r
-ShellCommandIsCommandOnList (\r
-  IN CONST  CHAR16                      *CommandString\r
+ShellCommandDynamicCommandExists (\r
+  IN CONST CHAR16 *CommandString\r
+  )\r
+{\r
+  return (BOOLEAN) ((ShellCommandFindDynamicCommand(CommandString) != NULL));\r
+}\r
+\r
+/**\r
+  Checks if a command is already on the internal command list.\r
+\r
+  @param[in] CommandString        The command string to check for on the list.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ShellCommandIsCommandOnInternalList(\r
+  IN CONST  CHAR16 *CommandString\r
   )\r
 {\r
   SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;\r
@@ -252,7 +337,52 @@ ShellCommandIsCommandOnList (
 }\r
 \r
 /**\r
-  Get the help text for a command.\r
+  Checks if a command exists, either internally or through the dynamic command protocol.\r
+\r
+  @param[in] CommandString        The command string to check for on the list.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ShellCommandIsCommandOnList(\r
+  IN CONST  CHAR16                      *CommandString\r
+  )\r
+{\r
+  if (ShellCommandIsCommandOnInternalList(CommandString)) {\r
+    return TRUE;\r
+  }\r
+\r
+  return ShellCommandDynamicCommandExists(CommandString);\r
+}\r
+\r
+/**\r
+ Get the help text for a dynamic command.\r
+\r
+  @param[in] CommandString        The command name.\r
+\r
+  @retval NULL  No help text was found.\r
+  @return       String of help text. Caller required to free.\r
+**/\r
+CHAR16*\r
+EFIAPI\r
+ShellCommandGetDynamicCommandHelp(\r
+  IN CONST  CHAR16                      *CommandString\r
+  )\r
+{\r
+  EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;\r
+\r
+  DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *)ShellCommandFindDynamicCommand(CommandString);\r
+  if (DynamicCommand == NULL) {\r
+    return (NULL);\r
+  }\r
+\r
+  //\r
+  // TODO: how to get proper language?\r
+  //\r
+  return DynamicCommand->GetHelp(DynamicCommand, "en"); \r
+}\r
+\r
+/**\r
+  Get the help text for an internal command.\r
 \r
   @param[in] CommandString        The command name.\r
 \r
@@ -261,7 +391,7 @@ ShellCommandIsCommandOnList (
 **/\r
 CHAR16*\r
 EFIAPI\r
-ShellCommandGetCommandHelp (\r
+ShellCommandGetInternalCommandHelp(\r
   IN CONST  CHAR16                      *CommandString\r
   )\r
 {\r
@@ -291,6 +421,31 @@ ShellCommandGetCommandHelp (
   return (NULL);\r
 }\r
 \r
+/**\r
+  Get the help text for a command.\r
+\r
+  @param[in] CommandString        The command name.\r
+\r
+  @retval NULL  No help text was found.\r
+  @return       String of help text.Caller reuiqred to free.\r
+**/\r
+CHAR16*\r
+EFIAPI\r
+ShellCommandGetCommandHelp (\r
+  IN CONST  CHAR16                      *CommandString\r
+  )\r
+{\r
+  CHAR16      *HelpStr;\r
+  HelpStr = ShellCommandGetInternalCommandHelp(CommandString);\r
+\r
+  if (HelpStr == NULL) {\r
+    HelpStr = ShellCommandGetDynamicCommandHelp(CommandString);\r
+  }\r
+\r
+  return HelpStr;\r
+}\r
+\r
+\r
 /**\r
   Registers handlers of type SHELL_RUN_COMMAND and\r
   SHELL_GET_MAN_FILENAME for each shell command.\r
@@ -390,14 +545,9 @@ ShellCommandRegisterCommandName (
   //\r
   Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY));\r
   ASSERT(Node != NULL);\r
-  Node->CommandString = AllocateZeroPool(StrSize(CommandString));\r
+  Node->CommandString = AllocateCopyPool(StrSize(CommandString), CommandString);\r
   ASSERT(Node->CommandString != NULL);\r
 \r
-  //\r
-  // populate the new struct\r
-  //\r
-  StrCpy(Node->CommandString, CommandString);\r
-\r
   Node->GetManFileName  = GetManFileName;\r
   Node->CommandHandler  = CommandHandler;\r
   Node->LastError       = CanAffectLE;\r
@@ -505,7 +655,8 @@ ShellCommandRunCommandHandler (
   IN OUT BOOLEAN                *CanAffectLE OPTIONAL\r
   )\r
 {\r
-  SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;\r
+  SHELL_COMMAND_INTERNAL_LIST_ENTRY   *Node;\r
+  EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;\r
 \r
   //\r
   // assert for NULL parameters\r
@@ -524,7 +675,7 @@ ShellCommandRunCommandHandler (
           gUnicodeCollation,\r
           (CHAR16*)CommandString,\r
           Node->CommandString) == 0\r
-       ){\r
+      ){\r
       if (CanAffectLE != NULL) {\r
         *CanAffectLE = Node->LastError;\r
       }\r
@@ -536,6 +687,20 @@ ShellCommandRunCommandHandler (
       return (RETURN_SUCCESS);\r
     }\r
   }\r
+\r
+  //\r
+  // An internal command was not found, try to find a dynamic command\r
+  //\r
+  DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *)ShellCommandFindDynamicCommand(CommandString);\r
+  if (DynamicCommand != NULL) {\r
+    if (RetVal != NULL) {\r
+      *RetVal = DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);\r
+    } else {\r
+      DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);\r
+    }\r
+    return (RETURN_SUCCESS);\r
+  }\r
+\r
   return (RETURN_NOT_FOUND);\r
 }\r
 \r
@@ -626,6 +791,9 @@ ShellCommandRegisterAlias (
   )\r
 {\r
   ALIAS_LIST *Node;\r
+  ALIAS_LIST *CommandAlias;\r
+  ALIAS_LIST *PrevCommandAlias; \r
+  INTN       LexicalMatchValue;\r
 \r
   //\r
   // Asserts for NULL\r
@@ -638,21 +806,42 @@ ShellCommandRegisterAlias (
   //\r
   Node = AllocateZeroPool(sizeof(ALIAS_LIST));\r
   ASSERT(Node != NULL);\r
-  Node->CommandString = AllocateZeroPool(StrSize(Command));\r
-  Node->Alias = AllocateZeroPool(StrSize(Alias));\r
+  Node->CommandString = AllocateCopyPool(StrSize(Command), Command);\r
+  Node->Alias = AllocateCopyPool(StrSize(Alias), Alias);\r
   ASSERT(Node->CommandString != NULL);\r
   ASSERT(Node->Alias != NULL);\r
 \r
-  //\r
-  // populate the new struct\r
-  //\r
-  StrCpy(Node->CommandString, Command);\r
-  StrCpy(Node->Alias        , Alias );\r
+  InsertHeadList (&mAliasList.Link, &Node->Link);\r
 \r
   //\r
-  // add the new struct to the list\r
+  // Move a new pre-defined registered alias to its sorted ordered location in the list\r
   //\r
-  InsertTailList (&mAliasList.Link, &Node->Link);\r
+  for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),\r
+         PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)\r
+       ; !IsNull (&mAliasList.Link, &CommandAlias->Link)\r
+       ; CommandAlias = (ALIAS_LIST *) GetNextNode (&mAliasList.Link, &CommandAlias->Link) ) {\r
+    //\r
+    // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry\r
+    //\r
+    LexicalMatchValue = gUnicodeCollation->StriColl (\r
+                                             gUnicodeCollation,\r
+                                             PrevCommandAlias->Alias,\r
+                                             CommandAlias->Alias\r
+                                             );\r
+\r
+    //\r
+    // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry\r
+    // is alphabetically greater than CommandAlias list entry\r
+    // \r
+    if (LexicalMatchValue > 0) {\r
+      CommandAlias = (ALIAS_LIST *) SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);\r
+    } else if (LexicalMatchValue < 0) {\r
+      //\r
+      // PrevCommandAlias entry is lexically lower than CommandAlias entry\r
+      //\r
+      break;\r
+    }\r
+  }\r
 \r
   return (RETURN_SUCCESS);\r
 }\r
@@ -722,7 +911,7 @@ ShellCommandIsOnAliasList(
 }\r
 \r
 /**\r
-  Function to determine current state of ECHO.  Echo determins if lines from scripts\r
+  Function to determine current state of ECHO.  Echo determines if lines from scripts\r
   and ECHO commands are enabled.\r
 \r
   @retval TRUE    Echo is currently enabled\r
@@ -738,7 +927,7 @@ ShellCommandGetEchoState(
 }\r
 \r
 /**\r
-  Function to set current state of ECHO.  Echo determins if lines from scripts\r
+  Function to set current state of ECHO.  Echo determines if lines from scripts\r
   and ECHO commands are enabled.\r
 \r
   If State is TRUE, Echo will be enabled.\r
@@ -995,12 +1184,11 @@ ShellCommandAddMapItemAndUpdatePath(
     Status = EFI_OUT_OF_RESOURCES;\r
   } else {\r
     MapListNode->Flags = Flags;\r
-    MapListNode->MapName = AllocateZeroPool(StrSize(Name));\r
+    MapListNode->MapName = AllocateCopyPool(StrSize(Name), Name);\r
     MapListNode->DevicePath = DuplicateDevicePath(DevicePath);\r
     if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){\r
       Status = EFI_OUT_OF_RESOURCES;\r
     } else {\r
-      StrCpy(MapListNode->MapName, Name);\r
       InsertTailList(&gShellMapList.Link, &MapListNode->Link);\r
     }\r
   }\r
@@ -1092,6 +1280,9 @@ ShellCommandCreateInitialMappingsAndPaths(
         ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)\r
        ){\r
           RemoveEntryList(&MapListNode->Link);\r
+          SHELL_FREE_NON_NULL(MapListNode->DevicePath);\r
+          SHELL_FREE_NON_NULL(MapListNode->MapName);\r
+          SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);\r
           FreePool(MapListNode);\r
     } // for loop\r
   }\r
@@ -1199,114 +1390,119 @@ ShellCommandCreateInitialMappingsAndPaths(
   }\r
 \r
   return (EFI_SUCCESS);\r
-}
-
-/**
-  Add mappings for any devices without one.  Do not change any existing maps.
-
-  @retval EFI_SUCCESS   The operation was successful.
-**/
-EFI_STATUS
-EFIAPI
-ShellCommandUpdateMapping (
-  VOID
-  )
-{
-  EFI_STATUS                Status;
-  EFI_HANDLE                *HandleList;
-  UINTN                     Count;
-  EFI_DEVICE_PATH_PROTOCOL  **DevicePathList;
-  CHAR16                    *NewDefaultName;
-  CHAR16                    *NewConsistName;
-  EFI_DEVICE_PATH_PROTOCOL  **ConsistMappingTable;
-
-  HandleList  = NULL;
-  Status      = EFI_SUCCESS;
-
-  //
-  // remove mappings that represent removed devices.
-  //
-
-  //
-  // Find each handle with Simple File System
-  //
-  HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
-  if (HandleList != NULL) {
-    //
-    // Do a count of the handles
-    //
-    for (Count = 0 ; HandleList[Count] != NULL ; Count++);
-
-    //
-    // Get all Device Paths
-    //
-    DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
-    ASSERT(DevicePathList != NULL);
-
-    for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
-      DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
-    }
-
-    //
-    // Sort all DevicePaths
-    //
-    PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
-
-    ShellCommandConsistMappingInitialize(&ConsistMappingTable);
-
-    //
-    // Assign new Mappings to remainders
-    //
-    for (Count = 0 ; HandleList[Count] != NULL && !EFI_ERROR(Status); Count++) {
-      //
-      // Skip ones that already have
-      //
-      if (gEfiShellProtocol->GetMapFromDevicePath(&DevicePathList[Count]) != NULL) {
-        continue;
-      }
-      //
-      // Get default name
-      //
-      NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
-      ASSERT(NewDefaultName != NULL);
-
-      //
-      // Call shell protocol SetMap function now...
-      //
-      Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewDefaultName);
-
-      if (!EFI_ERROR(Status)) {
-        //
-        // Now do consistent name
-        //
-        NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
-        if (NewConsistName != NULL) {
-          Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewConsistName);
-          FreePool(NewConsistName);
-        }
-      }
-
-      FreePool(NewDefaultName);
-    }
-    ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
-    SHELL_FREE_NON_NULL(HandleList);
-    SHELL_FREE_NON_NULL(DevicePathList);
-
-    HandleList = NULL;
-  } else {
-    Count = (UINTN)-1;
-  }
-  //
-  // Do it all over again for gEfiBlockIoProtocolGuid
-  //
-
-  return (Status);
-}
-
-/**
-  Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
-
-  @param[in] Handle     The SHELL_FILE_HANDLE to convert.
+}\r
+\r
+/**\r
+  Add mappings for any devices without one.  Do not change any existing maps.\r
+\r
+  @retval EFI_SUCCESS   The operation was successful.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ShellCommandUpdateMapping (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_HANDLE                *HandleList;\r
+  UINTN                     Count;\r
+  EFI_DEVICE_PATH_PROTOCOL  **DevicePathList;\r
+  CHAR16                    *NewDefaultName;\r
+  CHAR16                    *NewConsistName;\r
+  EFI_DEVICE_PATH_PROTOCOL  **ConsistMappingTable;\r
+\r
+  HandleList  = NULL;\r
+  Status      = EFI_SUCCESS;\r
+\r
+  //\r
+  // remove mappings that represent removed devices.\r
+  //\r
+\r
+  //\r
+  // Find each handle with Simple File System\r
+  //\r
+  HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);\r
+  if (HandleList != NULL) {\r
+    //\r
+    // Do a count of the handles\r
+    //\r
+    for (Count = 0 ; HandleList[Count] != NULL ; Count++);\r
+\r
+    //\r
+    // Get all Device Paths\r
+    //\r
+    DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);\r
+    if (DevicePathList == NULL) {\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+\r
+    for (Count = 0 ; HandleList[Count] != NULL ; Count++) {\r
+      DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);\r
+    }\r
+\r
+    //\r
+    // Sort all DevicePaths\r
+    //\r
+    PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);\r
+\r
+    ShellCommandConsistMappingInitialize(&ConsistMappingTable);\r
+\r
+    //\r
+    // Assign new Mappings to remainders\r
+    //\r
+    for (Count = 0 ; !EFI_ERROR(Status) && HandleList[Count] != NULL && !EFI_ERROR(Status); Count++) {\r
+      //\r
+      // Skip ones that already have\r
+      //\r
+      if (gEfiShellProtocol->GetMapFromDevicePath(&DevicePathList[Count]) != NULL) {\r
+        continue;\r
+      }\r
+      //\r
+      // Get default name\r
+      //\r
+      NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);\r
+      if (NewDefaultName == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        break;\r
+      }\r
+\r
+      //\r
+      // Call shell protocol SetMap function now...\r
+      //\r
+      Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewDefaultName);\r
+\r
+      if (!EFI_ERROR(Status)) {\r
+        //\r
+        // Now do consistent name\r
+        //\r
+        NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);\r
+        if (NewConsistName != NULL) {\r
+          Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewConsistName);\r
+          FreePool(NewConsistName);\r
+        }\r
+      }\r
+\r
+      FreePool(NewDefaultName);\r
+    }\r
+    ShellCommandConsistMappingUnInitialize(ConsistMappingTable);\r
+    SHELL_FREE_NON_NULL(HandleList);\r
+    SHELL_FREE_NON_NULL(DevicePathList);\r
+\r
+    HandleList = NULL;\r
+  } else {\r
+    Count = (UINTN)-1;\r
+  }\r
+  //\r
+  // Do it all over again for gEfiBlockIoProtocolGuid\r
+  //\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.\r
+\r
+  @param[in] Handle     The SHELL_FILE_HANDLE to convert.\r
 \r
   @return a EFI_FILE_PROTOCOL* representing the same file.\r
 **/\r
@@ -1345,11 +1541,14 @@ ConvertEfiFileProtocolToShellHandle(
     }\r
     NewNode             = AllocateZeroPool(sizeof(BUFFER_LIST));\r
     if (NewNode == NULL) {\r
+      SHELL_FREE_NON_NULL(Buffer);\r
       return (NULL);\r
     }\r
     Buffer->FileHandle  = (EFI_FILE_PROTOCOL*)Handle;\r
     Buffer->Path        = StrnCatGrow(&Buffer->Path, NULL, Path, 0);\r
     if (Buffer->Path == NULL) {\r
+      SHELL_FREE_NON_NULL(NewNode);\r
+      SHELL_FREE_NON_NULL(Buffer);\r
       return (NULL);\r
     }\r
     NewNode->Buffer     = Buffer;\r
@@ -1445,7 +1644,6 @@ ShellFileHandleEof(
 \r
   gEfiShellProtocol->GetFilePosition(Handle, &Pos);\r
   Info = gEfiShellProtocol->GetFileInfo (Handle);\r
-  ASSERT(Info != NULL);\r
   gEfiShellProtocol->SetFilePosition(Handle, Pos);\r
 \r
   if (Info == NULL) {\r
@@ -1487,7 +1685,6 @@ FreeBufferList (
       ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)\r
      ){\r
     RemoveEntryList(&BufferListEntry->Link);\r
-    ASSERT(BufferListEntry->Buffer != NULL);\r
     if (BufferListEntry->Buffer != NULL) {\r
       FreePool(BufferListEntry->Buffer);\r
     }\r
@@ -1495,3 +1692,53 @@ FreeBufferList (
   }\r
 }\r
 \r
+/**\r
+  Dump some hexadecimal data to the screen.\r
+\r
+  @param[in] Indent     How many spaces to indent the output.\r
+  @param[in] Offset     The offset of the printing.\r
+  @param[in] DataSize   The size in bytes of UserData.\r
+  @param[in] UserData   The data to print out.\r
+**/\r
+VOID\r
+DumpHex (\r
+  IN UINTN        Indent,\r
+  IN UINTN        Offset,\r
+  IN UINTN        DataSize,\r
+  IN VOID         *UserData\r
+  )\r
+{\r
+  UINT8 *Data;\r
+\r
+  CHAR8 Val[50];\r
+\r
+  CHAR8 Str[20];\r
+\r
+  UINT8 TempByte;\r
+  UINTN Size;\r
+  UINTN Index;\r
+\r
+  Data = UserData;\r
+  while (DataSize != 0) {\r
+    Size = 16;\r
+    if (Size > DataSize) {\r
+      Size = DataSize;\r
+    }\r
+\r
+    for (Index = 0; Index < Size; Index += 1) {\r
+      TempByte            = Data[Index];\r
+      Val[Index * 3 + 0]  = Hex[TempByte >> 4];\r
+      Val[Index * 3 + 1]  = Hex[TempByte & 0xF];\r
+      Val[Index * 3 + 2]  = (CHAR8) ((Index == 7) ? '-' : ' ');\r
+      Str[Index]          = (CHAR8) ((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);\r
+    }\r
+\r
+    Val[Index * 3]  = 0;\r
+    Str[Index]      = 0;\r
+    ShellPrintEx(-1, -1, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);\r
+\r
+    Data += Size;\r
+    Offset += Size;\r
+    DataSize -= Size;\r
+  }\r
+}\r