]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Application/CapsuleApp/CapsuleDump.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Application / CapsuleApp / CapsuleDump.c
index 7a3eb9436249deb31e6a356609d2897cea80bd28..b81c5b7b3afe05549608c7bc7522ba93f0a5da50 100644 (file)
@@ -1,14 +1,8 @@
 /** @file\r
   Dump Capsule image information.\r
 \r
-  Copyright (c) 2016 - 2018, 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
-  which accompanies this distribution.  The full text of the license may be found at\r
-  http://opensource.org/licenses/bsd-license.php\r
-\r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include <Library/UefiRuntimeServicesTableLib.h>\r
 #include <Library/UefiLib.h>\r
 #include <Library/PrintLib.h>\r
+#include <Library/FileHandleLib.h>\r
+#include <Library/SortLib.h>\r
+#include <Library/UefiBootManagerLib.h>\r
+#include <Library/DevicePathLib.h>\r
 #include <Protocol/FirmwareManagement.h>\r
+#include <Protocol/SimpleFileSystem.h>\r
+#include <Protocol/Shell.h>\r
 #include <Guid/ImageAuthentication.h>\r
 #include <Guid/CapsuleReport.h>\r
 #include <Guid/SystemResourceTable.h>\r
 #include <Guid/FmpCapsule.h>\r
+#include <Guid/CapsuleVendor.h>\r
 #include <IndustryStandard/WindowsUxCapsule.h>\r
 \r
+//\r
+// (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes)\r
+//\r
+#define MAX_FILE_NAME_SIZE   522\r
+#define MAX_FILE_NAME_LEN    (MAX_FILE_NAME_SIZE / sizeof(CHAR16))\r
+\r
 /**\r
   Read a file.\r
 \r
@@ -61,6 +68,37 @@ WriteFileFromBuffer (
   IN  VOID                                 *Buffer\r
   );\r
 \r
+/**\r
+  Get shell protocol.\r
+\r
+  @return Pointer to shell protocol.\r
+\r
+**/\r
+EFI_SHELL_PROTOCOL *\r
+GetShellProtocol (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Get SimpleFileSystem from boot option file path.\r
+\r
+  @param[in]  DevicePath     The file path of boot option\r
+  @param[out] FullPath       The full device path of boot device\r
+  @param[out] Fs             The file system within EfiSysPartition\r
+\r
+  @retval EFI_SUCCESS    Get file system successfully\r
+  @retval EFI_NOT_FOUND  No valid file system found\r
+  @retval others         Get file system failed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetEfiSysPartitionFromBootOptionFilePath (\r
+  IN  EFI_DEVICE_PATH_PROTOCOL         *DevicePath,\r
+  OUT EFI_DEVICE_PATH_PROTOCOL         **FullPath,\r
+  OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  **Fs\r
+  );\r
+\r
 /**\r
   Validate if it is valid capsule header\r
 \r
@@ -123,7 +161,7 @@ DumpFmpCapsule (
   UINTN                                         Count;\r
   EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *FmpImageHeader;\r
 \r
-  Print(L"[FmpCapusule]\n");\r
+  Print(L"[FmpCapsule]\n");\r
   Print(L"CapsuleHeader:\n");\r
   Print(L"  CapsuleGuid      - %g\n", &CapsuleHeader->CapsuleGuid);\r
   Print(L"  HeaderSize       - 0x%x\n", CapsuleHeader->HeaderSize);\r
@@ -504,6 +542,539 @@ DumpEsrtData (
   Print(L"\n");\r
 }\r
 \r
+\r
+/**\r
+  Dump capsule information from CapsuleHeader\r
+\r
+  @param[in] CapsuleHeader       The CapsuleHeader of the capsule image.\r
+\r
+  @retval EFI_SUCCESS            The capsule information is dumped.\r
+\r
+**/\r
+EFI_STATUS\r
+DumpCapsuleFromBuffer (\r
+  IN EFI_CAPSULE_HEADER                         *CapsuleHeader\r
+  )\r
+{\r
+  if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {\r
+    DumpUxCapsule (CapsuleHeader);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)) {\r
+    DumpFmpCapsule (CapsuleHeader);\r
+  }\r
+  if (IsNestedFmpCapsule (CapsuleHeader)) {\r
+    Print (L"[NestedCapusule]\n");\r
+    Print (L"CapsuleHeader:\n");\r
+    Print (L"  CapsuleGuid      - %g\n", &CapsuleHeader->CapsuleGuid);\r
+    Print (L"  HeaderSize       - 0x%x\n", CapsuleHeader->HeaderSize);\r
+    Print (L"  Flags            - 0x%x\n", CapsuleHeader->Flags);\r
+    Print (L"  CapsuleImageSize - 0x%x\n", CapsuleHeader->CapsuleImageSize);\r
+    DumpFmpCapsule ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize));\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This routine is called to upper case given unicode string.\r
+\r
+  @param[in]   Str              String to upper case\r
+\r
+  @retval upper cased string after process\r
+\r
+**/\r
+STATIC\r
+CHAR16 *\r
+UpperCaseString (\r
+  IN CHAR16 *Str\r
+  )\r
+{\r
+  CHAR16  *Cptr;\r
+\r
+  for (Cptr = Str; *Cptr != L'\0'; Cptr++) {\r
+    if (L'a' <= *Cptr && *Cptr <= L'z') {\r
+      *Cptr = *Cptr - L'a' + L'A';\r
+    }\r
+  }\r
+\r
+  return Str;\r
+}\r
+\r
+/**\r
+  This routine is used to return substring before period '.' or '\0'\r
+  Caller should respsonsible of substr space allocation & free\r
+\r
+  @param[in]   Str              String to check\r
+  @param[out]  SubStr           First part of string before period or '\0'\r
+  @param[out]  SubStrLen        Length of first part of string\r
+\r
+**/\r
+STATIC\r
+VOID\r
+GetSubStringBeforePeriod (\r
+  IN  CHAR16 *Str,\r
+  OUT CHAR16 *SubStr,\r
+  OUT UINTN  *SubStrLen\r
+  )\r
+{\r
+  UINTN Index;\r
+  for (Index = 0; Str[Index] != L'.' && Str[Index] != L'\0'; Index++) {\r
+    SubStr[Index] = Str[Index];\r
+  }\r
+\r
+  SubStr[Index] = L'\0';\r
+  *SubStrLen = Index;\r
+}\r
+\r
+/**\r
+  This routine pad the string in tail with input character.\r
+\r
+  @param[in]   StrBuf            Str buffer to be padded, should be enough room for\r
+  @param[in]   PadLen            Expected padding length\r
+  @param[in]   Character         Character used to pad\r
+\r
+**/\r
+STATIC\r
+VOID\r
+PadStrInTail (\r
+  IN CHAR16   *StrBuf,\r
+  IN UINTN    PadLen,\r
+  IN CHAR16   Character\r
+  )\r
+{\r
+  UINTN Index;\r
+\r
+  for (Index = 0; StrBuf[Index] != L'\0'; Index++);\r
+\r
+  while(PadLen != 0) {\r
+    StrBuf[Index] = Character;\r
+    Index++;\r
+    PadLen--;\r
+  }\r
+\r
+  StrBuf[Index] = L'\0';\r
+}\r
+\r
+/**\r
+  This routine find the offset of the last period '.' of string. if No period exists\r
+  function FileNameExtension is set to L'\0'\r
+\r
+  @param[in]   FileName           File name to split between last period\r
+  @param[out]  FileNameFirst      First FileName before last period\r
+  @param[out]  FileNameExtension  FileName after last period\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SplitFileNameExtension (\r
+  IN  CHAR16  *FileName,\r
+  OUT CHAR16  *FileNameFirst,\r
+  OUT CHAR16  *FileNameExtension\r
+  )\r
+{\r
+  UINTN Index;\r
+  UINTN StringLen;\r
+\r
+  StringLen = StrLen(FileName);\r
+  for (Index = StringLen; Index > 0 && FileName[Index] != L'.'; Index--);\r
+\r
+  //\r
+  // No period exists. No FileName Extension\r
+  //\r
+  if (Index == 0 && FileName[Index] != L'.') {\r
+    FileNameExtension[0] = L'\0';\r
+    Index = StringLen;\r
+  } else {\r
+    StrCpyS (FileNameExtension, MAX_FILE_NAME_LEN, &FileName[Index+1]);\r
+  }\r
+\r
+  //\r
+  // Copy First file name\r
+  //\r
+  StrnCpyS (FileNameFirst, MAX_FILE_NAME_LEN, FileName, Index);\r
+  FileNameFirst[Index] = L'\0';\r
+}\r
+\r
+/**\r
+  The function is called by PerformQuickSort to sort file name in alphabet.\r
+\r
+  @param[in] Left            The pointer to first buffer.\r
+  @param[in] Right           The pointer to second buffer.\r
+\r
+  @retval 0                  Buffer1 equal to Buffer2.\r
+  @return <0                 Buffer1 is less than Buffer2.\r
+  @return >0                 Buffer1 is greater than Buffer2.\r
+\r
+**/\r
+INTN\r
+EFIAPI\r
+CompareFileNameInAlphabet (\r
+  IN VOID                         *Left,\r
+  IN VOID                         *Right\r
+  )\r
+{\r
+  EFI_FILE_INFO  *FileInfo1;\r
+  EFI_FILE_INFO  *FileInfo2;\r
+  CHAR16         FileName1[MAX_FILE_NAME_SIZE];\r
+  CHAR16         FileExtension1[MAX_FILE_NAME_SIZE];\r
+  CHAR16         FileName2[MAX_FILE_NAME_SIZE];\r
+  CHAR16         FileExtension2[MAX_FILE_NAME_SIZE];\r
+  CHAR16         TempSubStr1[MAX_FILE_NAME_SIZE];\r
+  CHAR16         TempSubStr2[MAX_FILE_NAME_SIZE];\r
+  UINTN          SubStrLen1;\r
+  UINTN          SubStrLen2;\r
+  INTN           SubStrCmpResult;\r
+\r
+  FileInfo1 = (EFI_FILE_INFO *) (*(UINTN *)Left);\r
+  FileInfo2 = (EFI_FILE_INFO *) (*(UINTN *)Right);\r
+\r
+  SplitFileNameExtension (FileInfo1->FileName, FileName1, FileExtension1);\r
+  SplitFileNameExtension (FileInfo2->FileName, FileName2, FileExtension2);\r
+\r
+  UpperCaseString (FileName1);\r
+  UpperCaseString (FileName2);\r
+\r
+  GetSubStringBeforePeriod (FileName1, TempSubStr1, &SubStrLen1);\r
+  GetSubStringBeforePeriod (FileName2, TempSubStr2, &SubStrLen2);\r
+\r
+  if (SubStrLen1 > SubStrLen2) {\r
+    //\r
+    // Substr in NewFileName is longer.  Pad tail with SPACE\r
+    //\r
+    PadStrInTail (TempSubStr2, SubStrLen1 - SubStrLen2, L' ');\r
+  } else if (SubStrLen1 < SubStrLen2){\r
+    //\r
+    // Substr in ListedFileName is longer. Pad tail with SPACE\r
+    //\r
+    PadStrInTail (TempSubStr1, SubStrLen2 - SubStrLen1, L' ');\r
+  }\r
+\r
+  SubStrCmpResult = StrnCmp (TempSubStr1, TempSubStr2, MAX_FILE_NAME_LEN);\r
+  if (SubStrCmpResult != 0) {\r
+    return SubStrCmpResult;\r
+  }\r
+\r
+  UpperCaseString (FileExtension1);\r
+  UpperCaseString (FileExtension2);\r
+\r
+  return StrnCmp (FileExtension1, FileExtension2, MAX_FILE_NAME_LEN);\r
+}\r
+\r
+/**\r
+  Dump capsule information from disk.\r
+\r
+  @param[in] Fs                  The device path of disk.\r
+  @param[in] DumpCapsuleInfo     The flag to indicate whether to dump the capsule inforomation.\r
+\r
+  @retval EFI_SUCCESS            The capsule information is dumped.\r
+\r
+**/\r
+EFI_STATUS\r
+DumpCapsuleFromDisk (\r
+  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL            *Fs,\r
+  IN BOOLEAN                                    DumpCapsuleInfo\r
+  )\r
+{\r
+  EFI_STATUS                                    Status;\r
+  EFI_FILE                                      *Root;\r
+  EFI_FILE                                      *DirHandle;\r
+  EFI_FILE                                      *FileHandle;\r
+  UINTN                                         Index;\r
+  UINTN                                         FileSize;\r
+  VOID                                          *FileBuffer;\r
+  EFI_FILE_INFO                                 **FileInfoBuffer;\r
+  EFI_FILE_INFO                                 *FileInfo;\r
+  UINTN                                         FileCount;\r
+  BOOLEAN                                       NoFile;\r
+\r
+  DirHandle       = NULL;\r
+  FileHandle      = NULL;\r
+  Index           = 0;\r
+  FileInfoBuffer  = NULL;\r
+  FileInfo        = NULL;\r
+  FileCount       = 0;\r
+  NoFile          = FALSE;\r
+\r
+  Status = Fs->OpenVolume (Fs, &Root);\r
+  if (EFI_ERROR (Status)) {\r
+    Print (L"Cannot open volume. Status = %r\n", Status);\r
+    goto Done;\r
+  }\r
+\r
+  Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0);\r
+  if (EFI_ERROR (Status)) {\r
+    Print (L"Cannot open %s. Status = %r\n", EFI_CAPSULE_FILE_DIRECTORY, Status);\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Get file count first\r
+  //\r
+  do {\r
+    Status = FileHandleFindFirstFile (DirHandle, &FileInfo);\r
+    if (EFI_ERROR (Status) || FileInfo == NULL) {\r
+      Print (L"Get File Info Fail. Status = %r\n", Status);\r
+      goto Done;\r
+    }\r
+\r
+    if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) != 0) {\r
+      FileCount++;\r
+    }\r
+\r
+    Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile);\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"Get Next File Fail. Status = %r\n", Status);\r
+      goto Done;\r
+    }\r
+  } while (!NoFile);\r
+\r
+  if (FileCount == 0) {\r
+    Print (L"Error: No capsule file found!\n");\r
+    Status = EFI_NOT_FOUND;\r
+    goto Done;\r
+  }\r
+\r
+  FileInfoBuffer = AllocateZeroPool (sizeof (FileInfo) * FileCount);\r
+  if (FileInfoBuffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Done;\r
+  }\r
+  NoFile = FALSE;\r
+\r
+  //\r
+  // Get all file info\r
+  //\r
+  do {\r
+    Status = FileHandleFindFirstFile (DirHandle, &FileInfo);\r
+    if (EFI_ERROR (Status) || FileInfo == NULL) {\r
+      Print (L"Get File Info Fail. Status = %r\n", Status);\r
+      goto Done;\r
+    }\r
+\r
+    if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) != 0) {\r
+      FileInfoBuffer[Index++] = AllocateCopyPool ((UINTN)FileInfo->Size, FileInfo);\r
+    }\r
+\r
+    Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile);\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"Get Next File Fail. Status = %r\n", Status);\r
+      goto Done;\r
+    }\r
+  } while (!NoFile);\r
+\r
+  //\r
+  // Sort FileInfoBuffer by alphabet order\r
+  //\r
+  PerformQuickSort (\r
+    FileInfoBuffer,\r
+    FileCount,\r
+    sizeof (FileInfo),\r
+    (SORT_COMPARE) CompareFileNameInAlphabet\r
+    );\r
+\r
+  Print (L"The capsules will be performed by following order:\n");\r
+\r
+  for (Index = 0; Index < FileCount; Index++) {\r
+    Print (L"  %d.%s\n", Index + 1, FileInfoBuffer[Index]->FileName);\r
+  }\r
+\r
+  if (!DumpCapsuleInfo) {\r
+    Status = EFI_SUCCESS;\r
+    goto Done;\r
+  }\r
+\r
+  Print(L"The infomation of the capsules:\n");\r
+\r
+  for (Index = 0; Index < FileCount; Index++) {\r
+    FileHandle = NULL;\r
+    Status = DirHandle->Open (DirHandle, &FileHandle, FileInfoBuffer[Index]->FileName, EFI_FILE_MODE_READ, 0);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+\r
+    Status = FileHandleGetSize (FileHandle, (UINT64 *) &FileSize);\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status);\r
+      FileHandleClose (FileHandle);\r
+      goto Done;\r
+    }\r
+\r
+    FileBuffer = AllocatePool (FileSize);\r
+    if (FileBuffer == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Done;\r
+    }\r
+\r
+    Status = FileHandleRead (FileHandle, &FileSize, FileBuffer);\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status);\r
+      FileHandleClose (FileHandle);\r
+      FreePool (FileBuffer);\r
+      goto Done;\r
+    }\r
+\r
+    Print (L"**************************\n");\r
+    Print (L"  %d.%s:\n", Index + 1, FileInfoBuffer[Index]->FileName);\r
+    Print (L"**************************\n");\r
+    DumpCapsuleFromBuffer ((EFI_CAPSULE_HEADER *) FileBuffer);\r
+    FileHandleClose (FileHandle);\r
+    FreePool (FileBuffer);\r
+  }\r
+\r
+Done:\r
+  if (FileInfoBuffer != NULL) {\r
+    for (Index = 0; Index < FileCount; Index++) {\r
+      if (FileInfoBuffer[Index] != NULL) {\r
+        FreePool (FileInfoBuffer[Index]);\r
+      }\r
+    }\r
+    FreePool (FileInfoBuffer);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Dump capsule inforomation form Gather list.\r
+\r
+  @param[in]  BlockDescriptors The block descriptors for the capsule images\r
+  @param[in]  DumpCapsuleInfo  The flag to indicate whether to dump the capsule inforomation.\r
+\r
+**/\r
+VOID\r
+DumpBlockDescriptors (\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR   *BlockDescriptors,\r
+  IN BOOLEAN                        DumpCapsuleInfo\r
+  )\r
+{\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR      *TempBlockPtr;\r
+\r
+  TempBlockPtr = BlockDescriptors;\r
+\r
+  while (TRUE) {\r
+    if (TempBlockPtr->Length != 0) {\r
+      if (DumpCapsuleInfo) {\r
+        Print(L"******************************************************\n");\r
+      }\r
+      Print(L"Capsule data starts at 0x%08x with size 0x%08x\n", TempBlockPtr->Union.DataBlock, TempBlockPtr->Length);\r
+      if (DumpCapsuleInfo) {\r
+        Print(L"******************************************************\n");\r
+        DumpCapsuleFromBuffer ((EFI_CAPSULE_HEADER *) (UINTN) TempBlockPtr->Union.DataBlock);\r
+      }\r
+      TempBlockPtr += 1;\r
+    } else {\r
+      if (TempBlockPtr->Union.ContinuationPointer == (UINTN)NULL) {\r
+        break;\r
+      } else {\r
+        TempBlockPtr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockPtr->Union.ContinuationPointer;\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Dump Provisioned Capsule.\r
+\r
+  @param[in]  DumpCapsuleInfo  The flag to indicate whether to dump the capsule inforomation.\r
+\r
+**/\r
+VOID\r
+DumpProvisionedCapsule (\r
+  IN BOOLEAN                      DumpCapsuleInfo\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  CHAR16                          CapsuleVarName[30];\r
+  CHAR16                          *TempVarName;\r
+  UINTN                           Index;\r
+  EFI_PHYSICAL_ADDRESS            *CapsuleDataPtr64;\r
+  UINT16                          *BootNext;\r
+  CHAR16                          BootOptionName[20];\r
+  EFI_BOOT_MANAGER_LOAD_OPTION    BootNextOptionEntry;\r
+  EFI_DEVICE_PATH_PROTOCOL        *DevicePath;\r
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+  EFI_SHELL_PROTOCOL              *ShellProtocol;\r
+\r
+  Index             = 0;\r
+  CapsuleDataPtr64  = NULL;\r
+  BootNext          = NULL;\r
+\r
+  ShellProtocol = GetShellProtocol ();\r
+  if (ShellProtocol == NULL) {\r
+    Print (L"Get Shell Protocol Fail\n");\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Dump capsule provisioned on Memory\r
+  //\r
+  Print (L"#########################\n");\r
+  Print (L"### Capsule on Memory ###\n");\r
+  Print (L"#########################\n");\r
+  StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);\r
+  TempVarName = CapsuleVarName + StrLen (CapsuleVarName);\r
+  while (TRUE) {\r
+    if (Index > 0) {\r
+      UnicodeValueToStringS (\r
+        TempVarName,\r
+        sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),\r
+        0,\r
+        Index,\r
+        0\r
+        );\r
+    }\r
+\r
+    Status = GetVariable2 (\r
+              CapsuleVarName,\r
+              &gEfiCapsuleVendorGuid,\r
+              (VOID **) &CapsuleDataPtr64,\r
+              NULL\r
+              );\r
+    if (EFI_ERROR (Status) || CapsuleDataPtr64 == NULL) {\r
+      if (Index == 0) {\r
+        Print (L"No data.\n");\r
+      }\r
+      break;\r
+    }\r
+\r
+    Index++;\r
+    Print (L"Capsule Description at 0x%08x\n", *CapsuleDataPtr64);\r
+    DumpBlockDescriptors ((EFI_CAPSULE_BLOCK_DESCRIPTOR*) (UINTN) *CapsuleDataPtr64, DumpCapsuleInfo);\r
+  }\r
+\r
+  //\r
+  // Dump capsule provisioned on Disk\r
+  //\r
+  Print (L"#########################\n");\r
+  Print (L"### Capsule on Disk #####\n");\r
+  Print (L"#########################\n");\r
+  Status = GetVariable2 (\r
+             L"BootNext",\r
+             &gEfiGlobalVariableGuid,\r
+             (VOID **) &BootNext,\r
+             NULL\r
+            );\r
+  if (EFI_ERROR (Status) || BootNext == NULL) {\r
+    Print (L"Get BootNext Variable Fail. Status = %r\n", Status);\r
+  } else {\r
+    UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", *BootNext);\r
+    Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry);\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // Display description and device path\r
+      //\r
+      GetEfiSysPartitionFromBootOptionFilePath (BootNextOptionEntry.FilePath, &DevicePath, &Fs);\r
+      if(!EFI_ERROR (Status)) {\r
+        Print (L"Capsules are provisioned on BootOption: %s\n", BootNextOptionEntry.Description);\r
+        Print (L"    %s %s\n", ShellProtocol->GetMapFromDevicePath (&DevicePath), ConvertDevicePathToText(DevicePath, TRUE, TRUE));\r
+        DumpCapsuleFromDisk (Fs, DumpCapsuleInfo);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
 /**\r
   Dump FMP information.\r
 \r