public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Chen A Chen <chen.a.chen@intel.com>
To: edk2-devel@lists.01.org
Cc: Chen A Chen <chen.a.chen@intel.com>,
	Jian J Wang <jian.j.wang@intel.com>, Hao Wu <hao.a.wu@intel.com>,
	Zhang Chao B <chao.b.zhang@intel.com>
Subject: [PATCH v2 2/2] MdeModulePkg/CapsuleApp: Enhance CapsuleApp to support Capsule-on-Disk.
Date: Mon, 28 Jan 2019 09:21:15 +0800	[thread overview]
Message-ID: <20190128012115.11124-1-chen.a.chen@intel.com> (raw)

BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=1482

CapsuleApp is used for trigger capsule update.
Add -OD option in CapsuleApp to support doing capsule update via storage.
Add -F and -L options to support dumping information feature.

Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao Wu <hao.a.wu@intel.com>
Cc: Zhang Chao B <chao.b.zhang@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Chen A Chen <chen.a.chen@intel.com>
---
 MdeModulePkg/Application/CapsuleApp/CapsuleApp.c   | 153 +++-
 MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf |   8 +
 MdeModulePkg/Application/CapsuleApp/CapsuleDump.c  | 535 +++++++++++++-
 .../Application/CapsuleApp/CapsuleOnDisk.c         | 802 +++++++++++++++++++++
 MdeModulePkg/Include/Library/UefiBootManagerLib.h  |  19 +
 MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c   |  22 +
 6 files changed, 1524 insertions(+), 15 deletions(-)
 create mode 100644 MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c

diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
index 4d907242f3..ca9baa0a6a 100644
--- a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
+++ b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
@@ -23,6 +23,7 @@
 #include <Library/PrintLib.h>
 #include <Library/BmpSupportLib.h>
 #include <Protocol/GraphicsOutput.h>
+#include <Guid/GlobalVariable.h>
 #include <Guid/CapsuleReport.h>
 #include <Guid/SystemResourceTable.h>
 #include <Guid/FmpCapsule.h>
@@ -105,6 +106,44 @@ DumpEsrtData (
   VOID
   );
 
+/**
+  Dump Provisioned Capsule.
+
+  @param[in]  DumpCapsuleInfo  The flag to indicate whether to dump the capsule inforomation.
+**/
+VOID
+DumpProvisionedCapsule (
+  IN BOOLEAN                      DumpCapsuleInfo
+  );
+
+/**
+  Dump all EFI System Parition.
+**/
+VOID
+DumpAllEfiSysPartition (
+  VOID
+  );
+
+/**
+  Process Capsule On Disk.
+
+  @param[in]  CapsuleBuffer    An array of pointer to capsule images
+  @param[in]  FileSize         An array of UINTN to capsule images size
+  @param[in]  OrgFileName      An array of orginal capsule images name
+  @param[in]  NewFileName      An array of new capsule images name
+  @param[in]  CapsuleNum       The count of capsule images
+
+  @retval EFI_SUCCESS       Capsule on disk secceed.
+**/
+EFI_STATUS
+ProcessCapsuleOnDisk (
+  IN VOID                          **CapsuleBuffer,
+  IN UINTN                         *CapsuleBufferSize,
+  IN CHAR16                        **FilePath,
+  IN CHAR16                        *Map,
+  IN UINTN                         CapsuleNum
+  );
+
 /**
   Read a file.
 
@@ -799,19 +838,22 @@ PrintUsage (
   )
 {
   Print(L"CapsuleApp:  usage\n");
-  Print(L"  CapsuleApp <Capsule...> [-NR]\n");
+  Print(L"  CapsuleApp <Capsule...> [-NR] [-OD [FSx]]\n");
   Print(L"  CapsuleApp -S\n");
   Print(L"  CapsuleApp -C\n");
   Print(L"  CapsuleApp -P\n");
   Print(L"  CapsuleApp -E\n");
+  Print(L"  CapsuleApp -L\n");
+  Print(L"  CapsuleApp -L INFO\n");
+  Print(L"  CapsuleApp -F\n");
   Print(L"  CapsuleApp -G <BMP> -O <Capsule>\n");
   Print(L"  CapsuleApp -N <Capsule> -O <NestedCapsule>\n");
   Print(L"  CapsuleApp -D <Capsule>\n");
   Print(L"  CapsuleApp -P GET <ImageTypeId> <Index> -O <FileName>\n");
   Print(L"Parameter:\n");
-  Print(L"  -NR: No reset will be triggered for the capsule with\n");
-  Print(L"       CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without\n");
-  Print(L"       CAPSULE_FLAGS_INITIATE_RESET.\n");
+  Print(L"  -NR: No reset will be triggered for the capsule\n");
+  Print(L"       with CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without CAPSULE_FLAGS_INITIATE_RESET.\n");
+  Print(L"  -OD: Delivery of Capsules via file on Mass Storage device.");
   Print(L"  -S:  Dump capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");
   Print(L"       which is defined in UEFI specification.\n");
   Print(L"  -C:  Clear capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");
@@ -820,6 +862,8 @@ PrintUsage (
   Print(L"       ImageTypeId and Index (decimal format) to a file if 'GET'\n");
   Print(L"       option is used.\n");
   Print(L"  -E:  Dump UEFI ESRT table info.\n");
+  Print(L"  -L:  Dump provisioned capsule image information.\n");
+  Print(L"  -F:  Dump all EFI System Partition.\n");
   Print(L"  -G:  Convert a BMP file to be an UX capsule,\n");
   Print(L"       according to Windows Firmware Update document\n");
   Print(L"  -N:  Append a Capsule Header to an existing FMP capsule image\n");
@@ -851,7 +895,7 @@ UefiMain (
 {
   EFI_STATUS                    Status;
   RETURN_STATUS                 RStatus;
-  UINTN                         FileSize[MAX_CAPSULE_NUM];
+  UINTN                         CapsuleBufferSize[MAX_CAPSULE_NUM];
   VOID                          *CapsuleBuffer[MAX_CAPSULE_NUM];
   EFI_CAPSULE_BLOCK_DESCRIPTOR  *BlockDescriptors;
   EFI_CAPSULE_HEADER            *CapsuleHeaderArray[MAX_CAPSULE_NUM + 1];
@@ -859,9 +903,14 @@ UefiMain (
   EFI_RESET_TYPE                ResetType;
   BOOLEAN                       NeedReset;
   BOOLEAN                       NoReset;
+  BOOLEAN                       CapsuleOnDisk;
   CHAR16                        *CapsuleName;
+  CHAR16                        *CapsuleNames[MAX_CAPSULE_NUM];
+  CHAR16                        *MapFsStr;
   UINTN                         CapsuleNum;
   UINTN                         Index;
+  UINTN                         ParaOdIndex;
+  UINTN                         ParaNrIndex;
   EFI_GUID                      ImageTypeId;
   UINTN                         ImageIndex;
 
@@ -936,6 +985,20 @@ UefiMain (
     return EFI_SUCCESS;
   }
 
+  if (StrCmp(Argv[1], L"-L") == 0) {
+    if (Argc >= 3 && StrCmp(Argv[2], L"INFO") == 0) {
+      DumpProvisionedCapsule(TRUE);
+    } else {
+      DumpProvisionedCapsule(FALSE);
+    }
+    return EFI_SUCCESS;
+  }
+
+  if (StrCmp(Argv[1], L"-F") == 0) {
+    DumpAllEfiSysPartition();
+    return EFI_SUCCESS;
+  }
+
   if (Argv[1][0] == L'-') {
     Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[1]);
     return EFI_UNSUPPORTED;
@@ -943,12 +1006,56 @@ UefiMain (
 
   CapsuleFirstIndex = 1;
   NoReset = FALSE;
-  if ((Argc > 1) && (StrCmp(Argv[Argc - 1], L"-NR") == 0)) {
-    NoReset = TRUE;
-    CapsuleLastIndex = Argc - 2;
+  CapsuleOnDisk = FALSE;
+  ParaOdIndex = 0;
+  ParaNrIndex = 0;
+
+  for (Index = 1; Index < Argc; Index ++) {
+    if (StrCmp(Argv[Index], L"-OD") == 0) {
+      ParaOdIndex = Index;
+      CapsuleOnDisk = TRUE;
+    } else if (StrCmp(Argv[Index], L"-NR") == 0) {
+      ParaNrIndex = Index;
+      NoReset = TRUE;
+    }
+  }
+
+  if (ParaOdIndex != 0) {
+    if (ParaOdIndex == Argc - 1) {
+      MapFsStr = NULL;
+    } else if (ParaOdIndex == Argc - 2) {
+      MapFsStr = Argv[Argc-1];
+    } else {
+      Print (L"CapsuleApp: Invalid Position for -OD Options\n");
+      Status = EFI_INVALID_PARAMETER;
+      goto Done;
+    }
+
+    if (ParaNrIndex != 0) {
+      if (ParaNrIndex + 1 == ParaOdIndex) {
+        CapsuleLastIndex = ParaNrIndex - 1;
+      } else {
+        Print (L"CapsuleApp: Invalid Position for -NR Options\n");
+        Status = EFI_INVALID_PARAMETER;
+        goto Done;
+      }
+    } else {
+      CapsuleLastIndex = ParaOdIndex - 1;
+    }
   } else {
-    CapsuleLastIndex = Argc - 1;
+    if (ParaNrIndex != 0) {
+      if (ParaNrIndex == Argc -1) {
+        CapsuleLastIndex = ParaNrIndex - 1;
+      } else {
+        Print (L"CapsuleApp: Invalid Position for -NR Options\n");
+        Status = EFI_INVALID_PARAMETER;
+        goto Done;
+      }
+    } else {
+      CapsuleLastIndex = Argc - 1;
+    }
   }
+
   CapsuleNum = CapsuleLastIndex - CapsuleFirstIndex + 1;
 
   if (CapsuleFirstIndex > CapsuleLastIndex) {
@@ -961,26 +1068,27 @@ UefiMain (
   }
 
   ZeroMem(&CapsuleBuffer, sizeof(CapsuleBuffer));
-  ZeroMem(&FileSize, sizeof(FileSize));
+  ZeroMem(&CapsuleBufferSize, sizeof(CapsuleBufferSize));
   BlockDescriptors = NULL;
 
   for (Index = 0; Index < CapsuleNum; Index++) {
     CapsuleName = Argv[CapsuleFirstIndex + Index];
-    Status = ReadFileToBuffer(CapsuleName, &FileSize[Index], &CapsuleBuffer[Index]);
+    Status = ReadFileToBuffer(CapsuleName, &CapsuleBufferSize[Index], &CapsuleBuffer[Index]);
     if (EFI_ERROR(Status)) {
       Print(L"CapsuleApp: capsule image (%s) is not found.\n", CapsuleName);
       goto Done;
     }
-    if (!IsValidCapsuleHeader (CapsuleBuffer[Index], FileSize[Index])) {
+    if (!IsValidCapsuleHeader (CapsuleBuffer[Index], CapsuleBufferSize[Index])) {
       Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName);
       return EFI_INVALID_PARAMETER;
     }
+    CapsuleNames[Index] = CapsuleName;
   }
 
   //
   // Every capsule use 2 descriptor 1 for data 1 for end
   //
-  Status = BuildGatherList(CapsuleBuffer, FileSize, CapsuleNum, &BlockDescriptors);
+  Status = BuildGatherList(CapsuleBuffer, CapsuleBufferSize, CapsuleNum, &BlockDescriptors);
   if (EFI_ERROR(Status)) {
     goto Done;
   }
@@ -1007,13 +1115,30 @@ UefiMain (
   }
 
   for (Index = 0; Index < CapsuleNum; Index++) {
-    if (FileSize[Index] > MaxCapsuleSize) {
+    if (CapsuleBufferSize[Index] > MaxCapsuleSize) {
       Print (L"CapsuleApp: capsule is too large to update, %ld is allowed\n", MaxCapsuleSize);
       Status = EFI_UNSUPPORTED;
       goto Done;
     }
   }
 
+  //
+  // Check whether is capsule on disk.
+  //
+  if (CapsuleOnDisk) {
+    Status = ProcessCapsuleOnDisk (CapsuleBuffer, CapsuleBufferSize, CapsuleNames, MapFsStr, CapsuleNum);
+    if (Status != EFI_SUCCESS) {
+      Print (L"CapsuleApp: failed to update capsule - %r\n", Status);
+      goto Done;
+    } else {
+      if (!NoReset) {
+        gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL);
+      } else {
+        goto Done;
+      }
+    }
+  }
+
   //
   // Check whether the input capsule image has the flag of persist across system reset.
   //
diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf
index 8a21875286..0334e0caaf 100644
--- a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf
+++ b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf
@@ -33,6 +33,7 @@
 [Sources]
   CapsuleApp.c
   CapsuleDump.c
+  CapsuleOnDisk.c
   AppSupport.c
 
 [Packages]
@@ -40,16 +41,20 @@
   MdeModulePkg/MdeModulePkg.dec
 
 [Guids]
+  gEfiGlobalVariableGuid                 ## CONSUMES   ## GUID
   gEfiCapsuleReportGuid                  ## CONSUMES   ## GUID
   gEfiFmpCapsuleGuid                     ## CONSUMES   ## GUID
   gWindowsUxCapsuleGuid                  ## CONSUMES   ## GUID
   gEfiSystemResourceTableGuid            ## CONSUMES   ## GUID
+  gEfiCapsuleVendorGuid                  ## SOMETIMES_CONSUMES ## Variable:L"CapsuleUpdateData"
+  gEfiPartTypeSystemPartGuid             ## SOMETIMES_CONSUMES ## GUID
 
 [Protocols]
   gEfiGraphicsOutputProtocolGuid         ## CONSUMES
   gEfiFirmwareManagementProtocolGuid     ## CONSUMES
   gEfiShellParametersProtocolGuid        ## CONSUMES
   gEfiShellProtocolGuid                  ## CONSUMES
+  gEfiSimpleFileSystemProtocolGuid       ## SOMETIMES_CONSUMES
 
 [LibraryClasses]
   BaseLib
@@ -61,6 +66,9 @@
   UefiLib
   PrintLib
   BmpSupportLib
+  FileHandleLib
+  UefiBootManagerLib
+  SortLib
 
 [UserExtensions.TianoCore."ExtraFiles"]
   CapsuleAppExtra.uni
diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c b/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c
index 7a3eb94362..2af28d711c 100644
--- a/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c
+++ b/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c
@@ -21,13 +21,26 @@
 #include <Library/UefiRuntimeServicesTableLib.h>
 #include <Library/UefiLib.h>
 #include <Library/PrintLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/SortLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/DevicePathLib.h>
 #include <Protocol/FirmwareManagement.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/Shell.h>
 #include <Guid/ImageAuthentication.h>
 #include <Guid/CapsuleReport.h>
 #include <Guid/SystemResourceTable.h>
 #include <Guid/FmpCapsule.h>
+#include <Guid/CapsuleVendor.h>
 #include <IndustryStandard/WindowsUxCapsule.h>
 
+//
+// (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes)
+//
+#define MAX_FILE_NAME_SIZE   522
+#define MAX_FILE_NAME_LEN    (MAX_FILE_NAME_SIZE / sizeof(CHAR16))
+
 /**
   Read a file.
 
@@ -61,6 +74,37 @@ WriteFileFromBuffer (
   IN  VOID                                 *Buffer
   );
 
+/**
+  Get shell protocol.
+
+  @return Pointer to shell protocol.
+
+**/
+EFI_SHELL_PROTOCOL *
+GetShellProtocol (
+  VOID
+  );
+
+/**
+  Get SimpleFileSystem from boot option file path
+
+  @param[in]  DevicePath     The file path of boot option
+  @param[out] FullPath       The full device path of boot device
+  @param[out] Fs             The file system within EfiSysPartition
+
+  @retval EFI_SUCCESS    Get file system successfully
+  @retval EFI_NOT_FOUND  No valid file system found
+  @retval others         Get file system failed
+
+**/
+EFI_STATUS
+EFIAPI
+GetEfiSysPartitionFromBootOptionFilePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL         *DevicePath,
+  OUT EFI_DEVICE_PATH_PROTOCOL         **FullPath,
+  OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  **Fs
+  );
+
 /**
   Validate if it is valid capsule header
 
@@ -123,7 +167,7 @@ DumpFmpCapsule (
   UINTN                                         Count;
   EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *FmpImageHeader;
 
-  Print(L"[FmpCapusule]\n");
+  Print(L"[FmpCapsule]\n");
   Print(L"CapsuleHeader:\n");
   Print(L"  CapsuleGuid      - %g\n", &CapsuleHeader->CapsuleGuid);
   Print(L"  HeaderSize       - 0x%x\n", CapsuleHeader->HeaderSize);
@@ -504,6 +548,495 @@ DumpEsrtData (
   Print(L"\n");
 }
 
+
+/**
+  Dump capsule information from CapsuleHeader
+
+  @param[in] CapsuleHeader       The CapsuleHeader of the capsule image.
+
+  @retval EFI_SUCCESS            The capsule information is dumped.
+
+**/
+EFI_STATUS
+DumpCapsuleFromBuffer (
+  IN EFI_CAPSULE_HEADER                         *CapsuleHeader
+  )
+{
+  if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {
+    DumpUxCapsule (CapsuleHeader);
+    return EFI_SUCCESS;
+  }
+
+  if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)) {
+    DumpFmpCapsule (CapsuleHeader);
+  }
+  if (IsNestedFmpCapsule (CapsuleHeader)) {
+    Print (L"[NestedCapusule]\n");
+    Print (L"CapsuleHeader:\n");
+    Print (L"  CapsuleGuid      - %g\n", &CapsuleHeader->CapsuleGuid);
+    Print (L"  HeaderSize       - 0x%x\n", CapsuleHeader->HeaderSize);
+    Print (L"  Flags            - 0x%x\n", CapsuleHeader->Flags);
+    Print (L"  CapsuleImageSize - 0x%x\n", CapsuleHeader->CapsuleImageSize);
+    DumpFmpCapsule ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize));
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This routine is called to upper case given unicode string
+
+  @param[in]   Str              String to upper case
+
+  @retval upper cased string after process
+
+**/
+STATIC
+CHAR16 *
+UpperCaseString (
+  IN CHAR16 *Str
+  )
+{
+  CHAR16  *Cptr;
+
+  for (Cptr = Str; *Cptr; Cptr++) {
+    if (L'a' <= *Cptr && *Cptr <= L'z') {
+      *Cptr = *Cptr - L'a' + L'A';
+    }
+  }
+
+  return Str;
+}
+
+/**
+  This routine is used to return substring before period '.' or '\0'
+  Caller should respsonsible of substr space allocation & free
+
+  @param[in]   Str              String to check
+  @param[out]  SubStr           First part of string before period or '\0'
+  @param[out]  SubStrLen        Length of first part of string 
+
+**/
+STATIC
+VOID
+GetSubStringBeforePeriod (
+  IN  CHAR16 *Str, 
+  OUT CHAR16 *SubStr, 
+  OUT UINTN  *SubStrLen
+  )
+{
+  UINTN Index;
+  for (Index = 0; Str[Index] != L'.' && Str[Index] != L'\0'; Index++) {
+    SubStr[Index] = Str[Index]; 
+  }
+
+  SubStr[Index] = L'\0';
+  *SubStrLen = Index;
+}
+
+/**
+  This routine pad the string in tail with input character.
+
+  @param[in]   StrBuf            Str buffer to be padded, should be enough room for
+  @param[in]   PadLen            Expected padding length
+  @param[in]   Character         Character used to pad
+
+**/
+STATIC
+VOID
+PadStrInTail (
+  IN CHAR16   *StrBuf, 
+  IN UINTN    PadLen, 
+  IN CHAR16   Character
+  )
+{
+  UINTN Index;
+
+  for (Index = 0; StrBuf[Index] != L'\0'; Index++);
+
+  while(PadLen != 0) {
+    StrBuf[Index] = Character;
+    Index++;
+    PadLen--;
+  }
+
+  StrBuf[Index] = L'\0';
+ }
+
+/**
+  This routine find the offset of the last period '.' of string. if No period exists
+  function FileNameExtension is set to L'\0'
+
+  @param[in]   FileName          File name to split between last period
+  @param[out]  FileNameFirst      First FileName before last period
+  @param[out]  FileNameExtension  FileName after last period
+
+**/
+STATIC
+VOID
+SplitFileNameExtension (
+  IN  CHAR16  *FileName,
+  OUT CHAR16  *FileNameFirst,
+  OUT CHAR16  *FileNameExtension
+  )
+{
+  UINTN Index;
+  UINTN StringLen;
+
+  StringLen = StrLen(FileName);
+  for (Index = StringLen; Index > 0 && FileName[Index] != L'.'; Index--);
+
+  //
+  // No period exists. No FileName Extension
+  //
+  if (Index == 0 && FileName[Index] != L'.') {
+    FileNameExtension[0] = L'\0';
+    Index = StringLen;
+  } else {
+    StrCpyS (FileNameExtension, MAX_FILE_NAME_LEN, &FileName[Index+1]);
+  }
+
+  //
+  // Copy First file name
+  //
+  StrnCpyS (FileNameFirst, MAX_FILE_NAME_LEN, FileName, Index);
+  FileNameFirst[Index] = L'\0';
+}
+
+/**
+  The function is called by PerformQuickSort to sort file name in alphabet.
+
+  @param[in] Left            The pointer to first buffer.
+  @param[in] Right           The pointer to second buffer.
+
+  @retval 0                  Buffer1 equal to Buffer2.
+  @return <0                 Buffer1 is less than Buffer2.
+  @return >0                 Buffer1 is greater than Buffer2.
+
+**/
+INTN
+EFIAPI
+CompareFileNameInAlphabet (
+  IN EFI_PHYSICAL_ADDRESS  *Left,
+  IN EFI_PHYSICAL_ADDRESS  *Right
+  ) {
+  EFI_FILE_INFO  *FileInfo1;
+  EFI_FILE_INFO  *FileInfo2;
+  CHAR16         FileName1[MAX_FILE_NAME_SIZE];
+  CHAR16         FileExtension1[MAX_FILE_NAME_SIZE];
+  CHAR16         FileName2[MAX_FILE_NAME_SIZE];
+  CHAR16         FileExtension2[MAX_FILE_NAME_SIZE];
+  CHAR16         TempSubStr1[MAX_FILE_NAME_SIZE];
+  CHAR16         TempSubStr2[MAX_FILE_NAME_SIZE];
+  UINTN          SubStrLen1;
+  UINTN          SubStrLen2;
+  INTN           SubStrCmpResult;
+  
+  FileInfo1 = (EFI_FILE_INFO *) *Left;
+  FileInfo2 = (EFI_FILE_INFO *) *Right;
+
+  SplitFileNameExtension (FileInfo1->FileName, FileName1, FileExtension1);
+  SplitFileNameExtension (FileInfo2->FileName, FileName2, FileExtension2);
+
+  UpperCaseString (FileName1);
+  UpperCaseString (FileName2);
+
+  GetSubStringBeforePeriod (FileName1, TempSubStr1, &SubStrLen1);
+  GetSubStringBeforePeriod (FileName2, TempSubStr2, &SubStrLen2);
+
+  if (SubStrLen1 > SubStrLen2) {
+    //
+    // Substr in NewFileName is longer.  Pad tail with SPACE
+    //
+    PadStrInTail (TempSubStr2, SubStrLen1 - SubStrLen2, L' ');
+  } else if (SubStrLen1 < SubStrLen2){
+    //
+    // Substr in ListedFileName is longer. Pad tail with SPACE
+    //
+    PadStrInTail (TempSubStr1, SubStrLen2 - SubStrLen1, L' ');
+  }
+
+  SubStrCmpResult = StrnCmp (TempSubStr1, TempSubStr2, MAX_FILE_NAME_LEN);
+  if (SubStrCmpResult != 0) {
+    return SubStrCmpResult;
+  }
+
+  UpperCaseString (FileExtension1);
+  UpperCaseString (FileExtension2);
+
+  return StrnCmp (FileExtension1, FileExtension2, MAX_FILE_NAME_LEN);
+}
+
+/**
+  Dump capsule information from disk
+
+  @param[in] DevicePath          The device path of disk.
+  @param[in] DumpCapsuleInfo     The flag to indicate whether to dump the capsule inforomation.
+
+  @retval EFI_SUCCESS            The capsule information is dumped.
+
+**/
+EFI_STATUS
+DumpCapsuleFromDisk (
+  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL            *Fs,
+  IN BOOLEAN                                    DumpCapsuleInfo
+  )
+{
+  EFI_STATUS                                    Status;
+  EFI_FILE                                      *Root;
+  EFI_FILE                                      *DirHandle;
+  EFI_FILE                                      *FileHandle;
+  UINTN                                         Index;
+  UINTN                                         FileSize;
+  VOID                                          *FileBuffer;
+  EFI_FILE_INFO                                 **FileInfoBuffer;
+  EFI_FILE_INFO                                 *FileInfo;
+  UINTN                                         FileCount;
+  BOOLEAN                                       NoFile;
+
+  DirHandle  = NULL;
+  FileHandle = NULL;
+  Index      = 0;
+  FileCount  = 0;
+  NoFile     = FALSE;
+
+  Status = Fs->OpenVolume (Fs, &Root);
+  if (EFI_ERROR (Status)) {
+    Print (L"Cannot open volume. Status = %r\n", Status);
+    return EFI_NOT_FOUND;
+  }
+
+  Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FROM_FILE_DIR, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0);
+  if (EFI_ERROR (Status)) {
+    Print (L"Cannot open %s. Status = %r\n", EFI_CAPSULE_FROM_FILE_DIR, Status);
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Get file count first
+  //
+  for ( Status = FileHandleFindFirstFile (DirHandle, &FileInfo)
+      ; !EFI_ERROR(Status) && !NoFile
+      ; Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile)
+     ){
+    if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) == 0) {
+      continue;
+    }
+    FileCount ++;
+  }
+
+  if (FileCount == 0) {
+    Print (L"Error: No capsule file found!\n");
+    return EFI_NOT_FOUND;
+  }
+
+  FileInfoBuffer = AllocatePool (sizeof(FileInfo) * FileCount);
+  NoFile = FALSE;
+
+  //
+  // Get all file info
+  //
+  for ( Status = FileHandleFindFirstFile (DirHandle, &FileInfo)
+      ; !EFI_ERROR (Status) && !NoFile
+      ; Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile)
+     ){
+    if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) == 0) {
+      continue;
+    }
+    FileInfoBuffer[Index ++] = AllocateCopyPool (FileInfo->Size, FileInfo);
+  }
+
+  //
+  // Sort FileInfoBuffer by alphabet order
+  //
+  PerformQuickSort (
+    FileInfoBuffer,
+    FileCount,
+    sizeof (FileInfo),
+    (SORT_COMPARE) CompareFileNameInAlphabet
+    );
+
+  Print (L"The capsules will be performed by following order:\n");
+
+  for (Index = 0; Index < FileCount; Index ++) {
+    Print (L"  %d.%s\n", Index + 1, FileInfoBuffer[Index]->FileName);
+  }
+
+  if (!DumpCapsuleInfo) {
+    return EFI_SUCCESS;
+  }
+
+  Print(L"The infomation of the capsules:\n");
+
+  for (Index = 0; Index < FileCount; Index ++) {
+    FileHandle = NULL;
+    Status = DirHandle->Open (DirHandle, &FileHandle, FileInfoBuffer[Index]->FileName, EFI_FILE_MODE_READ, 0);
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    Status = FileHandleGetSize (FileHandle, (UINT64 *) &FileSize);
+    if (EFI_ERROR (Status)) {
+      Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status);
+      FileHandleClose (FileHandle);
+      return Status;
+    }
+
+    FileBuffer = AllocatePool (FileSize);
+    if (FileBuffer == NULL) {
+      return RETURN_OUT_OF_RESOURCES;
+    }
+
+    Status = FileHandleRead (FileHandle, &FileSize, FileBuffer);
+    if (EFI_ERROR (Status)) {
+      Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status);
+      FreePool (FileBuffer);
+      FileHandleClose (FileHandle);
+      return Status;
+    }
+
+    Print (L"**************************\n");
+    Print (L"  %d.%s:\n", Index + 1, FileInfoBuffer[Index]->FileName);
+    Print (L"**************************\n");
+    DumpCapsuleFromBuffer ((EFI_CAPSULE_HEADER *) FileBuffer);
+    FileHandleClose (FileHandle);
+    FreePool (FileBuffer);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Dump capsule inforomation form Gather list.
+
+  @param[in]  BlockDescriptors The block descriptors for the capsule images
+  @param[in]  DumpCapsuleInfo  The flag to indicate whether to dump the capsule inforomation.
+
+**/
+VOID
+DumpBlockDescriptors (
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR   *BlockDescriptors,
+  IN BOOLEAN                        DumpCapsuleInfo
+  )
+{
+  EFI_CAPSULE_BLOCK_DESCRIPTOR      *TempBlockPtr;
+
+  TempBlockPtr = BlockDescriptors;
+
+  while (TRUE) {
+    if (TempBlockPtr->Length != 0) {
+      if (DumpCapsuleInfo) {
+        Print(L"******************************************************\n");
+      }
+      Print(L"Capsule data starts at 0x%08x with size 0x%08x\n", TempBlockPtr->Union.DataBlock, TempBlockPtr->Length);
+      if (DumpCapsuleInfo) {
+        Print(L"******************************************************\n");
+        DumpCapsuleFromBuffer ((EFI_CAPSULE_HEADER *) (UINTN) TempBlockPtr->Union.DataBlock);
+      }
+      TempBlockPtr += 1;
+    } else {
+      if (TempBlockPtr->Union.ContinuationPointer == (UINTN)NULL) {
+        break;
+      } else {
+        TempBlockPtr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockPtr->Union.ContinuationPointer;
+      }
+    }
+  }
+}
+
+/**
+  Dump Provisioned Capsule.
+
+  @param[in]  DumpCapsuleInfo  The flag to indicate whether to dump the capsule inforomation.
+
+**/
+VOID
+DumpProvisionedCapsule (
+  IN BOOLEAN                      DumpCapsuleInfo
+  )
+{
+  EFI_STATUS                      Status;
+  CHAR16                          CapsuleVarName[30];
+  CHAR16                          *TempVarName;
+  UINTN                           Index;
+  EFI_PHYSICAL_ADDRESS            *CapsuleDataPtr64;
+  UINT16                          *BootNext;
+  CHAR16                          BootOptionName[20];
+  EFI_BOOT_MANAGER_LOAD_OPTION    BootNextOptionEntry;
+  EFI_DEVICE_PATH_PROTOCOL        *DevicePath;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+  EFI_SHELL_PROTOCOL              *ShellProtocol;
+
+  ShellProtocol = GetShellProtocol ();
+
+  Index = 0;
+
+  //
+  // Dump capsule provisioned on Memory
+  // 
+  Print (L"#########################\n");
+  Print (L"### Capsule on Memory ###\n");
+  Print (L"#########################\n");
+  StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);
+  TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
+  while (TRUE) {
+    if (Index > 0) {
+      UnicodeValueToStringS (
+        TempVarName,
+        sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),
+        0,
+        Index,
+        0
+        );
+    }
+
+    Status = GetVariable2 (
+              CapsuleVarName,
+              &gEfiCapsuleVendorGuid,
+              (VOID **) &CapsuleDataPtr64,
+              NULL
+              );
+    if (EFI_ERROR (Status)) {
+      if (Index == 0) {
+        Print (L"No data.\n");
+      }
+      break;
+    } else {
+      Index ++;
+      Print (L"Capsule Description at 0x%08x\n", *CapsuleDataPtr64);
+      DumpBlockDescriptors ((EFI_CAPSULE_BLOCK_DESCRIPTOR*) (UINTN) *CapsuleDataPtr64, DumpCapsuleInfo);
+    }
+  }
+  
+  //
+  // Dump capsule provisioned on Disk
+  //
+  Print (L"#########################\n");
+  Print (L"### Capsule on Disk #####\n");
+  Print (L"#########################\n");
+  Status = GetVariable2 (
+             L"BootNext",
+             &gEfiGlobalVariableGuid,
+             (VOID **) &BootNext,
+             NULL
+            );
+  if (!EFI_ERROR (Status)) {
+    UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", *BootNext);
+    Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry);
+    if (!EFI_ERROR (Status)) {
+      //
+      // Display description and device path
+      //
+      GetEfiSysPartitionFromBootOptionFilePath (BootNextOptionEntry.FilePath, &DevicePath, &Fs);
+      if(!EFI_ERROR (Status)) {
+        Print (L"Capsules are provisioned on BootOption: %s\n", BootNextOptionEntry.Description);
+        Print (L"    %s %s\n", ShellProtocol->GetMapFromDevicePath (&DevicePath), ConvertDevicePathToText(DevicePath, TRUE, TRUE));
+        DumpCapsuleFromDisk (Fs, DumpCapsuleInfo);
+      }
+    }
+  }
+}
+
 /**
   Dump FMP information.
 
diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c b/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c
new file mode 100644
index 0000000000..ad9ad9ba87
--- /dev/null
+++ b/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c
@@ -0,0 +1,802 @@
+/** @file
+  Process Capsule On Disk.
+
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/Shell.h>
+#include <Guid/FileInfo.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/Gpt.h>
+
+EFI_GUID mCapsuleOnDiskBootOptionGuid = { 0x4CC29BB7, 0x2413, 0x40A2, { 0xB0, 0x6D, 0x25, 0x3E, 0x37, 0x10, 0xF5, 0x32 } };
+
+/**
+  Get shell protocol.
+
+  @return Pointer to shell protocol.
+
+**/
+EFI_SHELL_PROTOCOL *
+GetShellProtocol (
+  VOID
+  );
+
+/**
+  Get file name from file path
+
+  @return Pointer to file name.
+
+**/
+CHAR16 *
+GetFileNameFromPath (
+  CHAR16                            *FilePath
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_SHELL_PROTOCOL                *ShellProtocol;
+  SHELL_FILE_HANDLE                 Handle;
+  EFI_FILE_INFO                     *FileInfo;
+
+  ShellProtocol = GetShellProtocol ();
+  if (ShellProtocol == NULL) {
+    return NULL;
+  }
+
+  //
+  // Open file by FileName.
+  //
+  Status = ShellProtocol->OpenFileByName (
+                            FilePath,
+                            &Handle,
+                            EFI_FILE_MODE_READ
+                            );
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  //
+  // Get file name from EFI_FILE_INFO.
+  //
+  FileInfo = ShellProtocol->GetFileInfo (Handle);
+  ShellProtocol->CloseFile (Handle);
+  if (FileInfo == NULL) {
+    return NULL;
+  }
+
+  return FileInfo->FileName;
+}
+
+/**
+  Check if the device path is EFI system parition.
+
+  @retval TRUE    DevicePath is a device path for ESP.
+  @retval FALSE   DevicePath is not a device path for ESP.
+
+**/
+BOOLEAN
+IsEfiSysPartitionDevicePath (
+  EFI_DEVICE_PATH_PROTOCOL   *DevicePath
+  ) 
+{
+  EFI_STATUS                 Status;
+  EFI_DEVICE_PATH_PROTOCOL   *TempDevicePath;
+  HARDDRIVE_DEVICE_PATH      *Hd;
+  EFI_HANDLE                 Handle;
+
+  //
+  // Check if the device path contains GPT node 
+  //
+  TempDevicePath = DevicePath;
+  
+  while (!IsDevicePathEnd (TempDevicePath)) {
+    if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&
+      (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {
+      Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;
+      if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {
+        break;
+      }
+    }
+    TempDevicePath = NextDevicePathNode (TempDevicePath);
+  }
+
+  if (!IsDevicePathEnd (TempDevicePath)) {
+    //
+    // Search for EFI system partition protocol on full device path in Boot Option 
+    //
+    Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);
+    return EFI_ERROR (Status) ? FALSE : TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/**
+  Dump all EFI System Parition.
+
+**/
+VOID
+DumpAllEfiSysPartition (
+  VOID
+  )
+{
+  EFI_HANDLE                 *SimpleFileSystemHandles;
+  UINTN                      NumberSimpleFileSystemHandles;
+  UINTN                      Index;
+  EFI_DEVICE_PATH_PROTOCOL   *DevicePath;
+  UINTN                      NumberEfiSystemPartitions;
+  EFI_SHELL_PROTOCOL         *ShellProtocol;
+
+  ShellProtocol = GetShellProtocol ();
+  NumberEfiSystemPartitions = 0;
+
+  Print (L"EFI System Partition list:\n");
+
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiSimpleFileSystemProtocolGuid,
+         NULL,
+         &NumberSimpleFileSystemHandles,
+         &SimpleFileSystemHandles
+         );
+
+  for (Index = 0; Index < NumberSimpleFileSystemHandles; Index ++) {
+    DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
+    if (IsEfiSysPartitionDevicePath (DevicePath)) {
+      NumberEfiSystemPartitions ++;
+      Print(L"    %s\n        %s\n", ShellProtocol->GetMapFromDevicePath (&DevicePath), ConvertDevicePathToText (DevicePath, TRUE, TRUE));
+    }
+  }
+
+  if (NumberEfiSystemPartitions == 0) {
+    Print(L"    No ESP found.\n");
+  }
+}
+
+/**
+  Check if capsule is provisioned.
+
+  @retval TRUE    Capsule is provisioned previously.
+  @retval FALSE   No capsule is provisioned.
+
+**/
+BOOLEAN
+IsCapsuleProvisioned (
+  VOID
+  )
+{
+  EFI_STATUS            Status;
+  UINT64                OsIndication;
+  UINTN                 DataSize;
+
+  OsIndication = 0;
+  DataSize = sizeof(UINT64);
+  Status = gRT->GetVariable (
+                  L"OsIndications",
+                  &gEfiGlobalVariableGuid,
+                  NULL,
+                  &DataSize,
+                  &OsIndication
+                  );
+  if (!EFI_ERROR (Status) && 
+      (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+  Get one active Efi System Partition
+
+  @param[out] FsDevicePath   The device path of Fs
+  @param[out] Fs             The file system within EfiSysPartition
+
+  @retval EFI_SUCCESS     Get file system successfully
+  @retval EFI_NOT_FOUND   No valid file system found
+
+**/
+EFI_STATUS
+GetEfiSysPartition (
+  OUT EFI_DEVICE_PATH_PROTOCOL         **FsDevicePath,
+  OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  **Fs
+  )
+{
+  EFI_HANDLE                 *SimpleFileSystemHandles;
+  UINTN                      NumberSimpleFileSystemHandles;
+  UINTN                      Index;
+  EFI_DEVICE_PATH_PROTOCOL   *DevicePath;
+  EFI_STATUS                 Status;
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  NULL,
+                  &NumberSimpleFileSystemHandles,
+                  &SimpleFileSystemHandles
+                  );
+
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_FOUND;
+  }
+
+  for (Index = 0; Index < NumberSimpleFileSystemHandles; Index ++) {
+    DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
+    if (IsEfiSysPartitionDevicePath (DevicePath)) {
+      Status = gBS->HandleProtocol (SimpleFileSystemHandles[Index], &gEfiSimpleFileSystemProtocolGuid, Fs);
+      if (!EFI_ERROR (Status)) {
+        *FsDevicePath = DevicePath;
+        return EFI_SUCCESS;
+      }
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Check if Active Efi System Partition within GPT is in the device path
+
+  @param[in]  DevicePath     The device path
+  @param[out] FullPath       The device path of Fs
+  @param[out] Fs             The file system within EfiSysPartition
+
+  @retval EFI_SUCCESS    Get file system successfully
+  @retval EFI_NOT_FOUND  No valid file system found
+  @retval others         Get file system failed
+
+**/
+EFI_STATUS
+GetEfiSysPartitionFromDevPath (
+  IN  EFI_DEVICE_PATH_PROTOCOL        *DevicePath,
+  OUT EFI_DEVICE_PATH_PROTOCOL        **FsDevicePath,
+  OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs
+  )
+{
+  EFI_STATUS                 Status;
+  EFI_DEVICE_PATH_PROTOCOL   *TempDevicePath;
+  HARDDRIVE_DEVICE_PATH      *Hd;
+  EFI_HANDLE                 Handle;
+
+  //
+  // Check if the device path contains GPT node 
+  //
+  TempDevicePath = DevicePath;
+  while (!IsDevicePathEnd (TempDevicePath)) {
+    if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&
+       (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {
+      Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;
+      if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {
+        break;
+      }
+    }
+    TempDevicePath = NextDevicePathNode (TempDevicePath);
+  }
+
+  if (!IsDevicePathEnd (TempDevicePath)) {
+    //
+    // Search for EFI system partition protocol on full device path in Boot Option 
+    //
+    Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);
+
+    //
+    // Search for simple file system on this handler
+    //
+    if (!EFI_ERROR (Status)) {
+      Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, Fs);
+      if (!EFI_ERROR (Status)) {
+        *FsDevicePath = DevicePathFromHandle (Handle);
+        return EFI_SUCCESS;
+      }
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Get SimpleFileSystem from boot option file path
+
+  @param[in]  DevicePath     The file path of boot option
+  @param[out] FullPath       The full device path of boot device
+  @param[out] Fs             The file system within EfiSysPartition
+
+  @retval EFI_SUCCESS    Get file system successfully
+  @retval EFI_NOT_FOUND  No valid file system found
+  @retval others         Get file system failed
+
+**/
+EFI_STATUS
+EFIAPI
+GetEfiSysPartitionFromBootOptionFilePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL         *DevicePath,
+  OUT EFI_DEVICE_PATH_PROTOCOL         **FullPath,
+  OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  **Fs
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_DEVICE_PATH_PROTOCOL          *CurFullPath;
+  EFI_DEVICE_PATH_PROTOCOL          *PreFullPath;
+  EFI_DEVICE_PATH_PROTOCOL          *FsFullPath;
+
+  CurFullPath = NULL;
+  FsFullPath = NULL;
+  //
+  // Try every full device Path generated from bootoption 
+  //
+  do {
+    PreFullPath = CurFullPath;
+    CurFullPath = EfiBootManagerGetNextFullDevicePath (DevicePath, CurFullPath);
+
+    if (PreFullPath != NULL) {
+      FreePool (PreFullPath);
+    }
+
+    if (CurFullPath == NULL) {
+      //
+      // No Active EFI system partition is found in BootOption device path
+      //
+      Status = EFI_NOT_FOUND;
+      break;
+    }
+
+    DEBUG_CODE (
+      CHAR16 *DevicePathStr;
+
+      DevicePathStr = ConvertDevicePathToText (CurFullPath, TRUE, TRUE);
+      if (DevicePathStr != NULL){
+        DEBUG ((DEBUG_INFO, "Full device path %s\n", DevicePathStr));
+        FreePool (DevicePathStr);
+      } 
+    );
+
+    Status = GetEfiSysPartitionFromDevPath (CurFullPath, &FsFullPath, Fs);
+  } while (EFI_ERROR (Status));
+
+  if (*Fs != NULL) {
+    *FullPath = FsFullPath;
+    return EFI_SUCCESS;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+}
+
+/**
+  Get a valid SimpleFileSystem within EFI system partition
+
+  @param[In]  Map             The FS mapping capsule write to
+  @param[out] BootNext        The value of BootNext Variable
+  @param[out] Handle          The file system handle
+  @param[out] UpdateBootNext  The flag to indicate whether update BootNext Variable
+
+  @retval EFI_SUCCESS    Get FS successfully
+  @retval EFI_NOT_FOUND  No valid FS found
+  @retval others         Get FS failed
+
+**/
+EFI_STATUS
+EFIAPI
+GetUpdateFileSystem (
+  IN  CHAR16                           *Map,
+  OUT UINT16                           *BootNext,
+  OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  **Fs,
+  OUT BOOLEAN                          *UpdateBootNext
+)
+{
+  EFI_STATUS                      Status;
+  CHAR16                          BootOptionName[20];
+  UINTN                           Index;
+  CONST EFI_DEVICE_PATH_PROTOCOL  *MappedDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL        *DevicePath;
+  EFI_DEVICE_PATH_PROTOCOL        *FullPath;
+  UINT16                          *BootNextData;
+  EFI_BOOT_MANAGER_LOAD_OPTION    BootNextOption;
+  EFI_BOOT_MANAGER_LOAD_OPTION    *BootOptionBuffer;
+  UINTN                           BootOptionCount;
+  EFI_SHELL_PROTOCOL              *ShellProtocol;
+  EFI_BOOT_MANAGER_LOAD_OPTION    NewOption;
+
+  MappedDevicePath = NULL;
+  ShellProtocol = GetShellProtocol ();
+
+  //
+  // 1. If Fs is not assigned and there are capsule provisioned before,
+  // Get EFI system partition from BootNext.
+  //
+  if (IsCapsuleProvisioned () && Map == NULL) {
+    Status = GetVariable2 (
+               L"BootNext",
+               &gEfiGlobalVariableGuid,
+               &BootNextData,
+               NULL
+               );
+    if (!EFI_ERROR (Status)) {
+      UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", *BootNextData);
+      Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOption);
+      if (!EFI_ERROR (Status)) {
+        DevicePath = BootNextOption.FilePath;
+        Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs);
+        if (!EFI_ERROR (Status)) {
+          *UpdateBootNext = FALSE;
+          Print(L"Get EFI system partition from BootNext : %s\n", BootNextOption.Description);
+          Print(L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE));
+          return EFI_SUCCESS;
+        }
+      }
+    }
+  }
+
+  //
+  // Check if Map is valid.
+  //
+  if (Map != NULL) {
+    MappedDevicePath = ShellProtocol->GetDevicePathFromMap (Map);
+    if (MappedDevicePath == NULL) {
+      Print(L"'%s' is not a valid mapping.\n", Map);
+      return EFI_INVALID_PARAMETER;
+    } else if (!IsEfiSysPartitionDevicePath (DuplicateDevicePath (MappedDevicePath))) {
+      Print(L"'%s' is not a EFI System Partition.\n", Map);
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // 2. Get EFI system partition form boot options.
+  //
+  BootOptionBuffer = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+  if (BootOptionCount == 0 && Map == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  for (Index = 0; Index < BootOptionCount; Index ++) {
+    //
+    // Get the boot option from the link list
+    //
+    DevicePath  = BootOptionBuffer[Index].FilePath;
+
+    //
+    // Skip inactive or legacy boot options
+    //
+    if ((BootOptionBuffer[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 ||
+        DevicePathType (DevicePath) == BBS_DEVICE_PATH) {
+      continue;
+    }
+
+    DEBUG_CODE (
+      CHAR16 *DevicePathStr;
+
+      DevicePathStr = ConvertDevicePathToText (DevicePath, TRUE, TRUE);
+      if (DevicePathStr != NULL){
+        DEBUG ((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr));
+        FreePool (DevicePathStr);
+      } else {
+        DEBUG ((DEBUG_INFO, "DevicePathToStr failed\n"));
+      }
+    );
+
+    Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs);
+    if (!EFI_ERROR (Status)) {
+      if (Map == NULL) {
+        *BootNext = (UINT16) BootOptionBuffer[Index].OptionNumber;
+        *UpdateBootNext = TRUE;
+        Print (L"Found EFI system partition on Boot%04x: %s\n", *BootNext, BootOptionBuffer[Index].Description);
+        Print (L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE));
+        return EFI_SUCCESS;
+      }
+
+      if (StrnCmp (Map, ShellProtocol->GetMapFromDevicePath (&FullPath), StrLen (Map)) == 0) {
+        *BootNext = (UINT16) BootOptionBuffer[Index].OptionNumber;
+        *UpdateBootNext = TRUE;
+        Print (L"Found Boot Option on %s : %s\n", Map, BootOptionBuffer[Index].Description);
+        return EFI_SUCCESS;
+      }
+    }
+  }
+
+  //
+  // 3. If no ESP is found on boot option, try to find a ESP and create boot option for it.
+  //
+  if (Map != NULL) {
+    //
+    // If map is assigned, try to get ESP from mapped Fs.
+    //
+    DevicePath = DuplicateDevicePath (MappedDevicePath);
+    Status = GetEfiSysPartitionFromDevPath (DevicePath, &FullPath, Fs);
+    if (EFI_ERROR (Status)) {
+      Print (L"Error: Cannot get EFI system partiion from '%s' - %r\n", Map, Status);
+      return EFI_NOT_FOUND;
+    }
+    Print (L"Warning: Cannot find Boot Option on '%s'!\n", Map);
+  } else {
+    Status = GetEfiSysPartition (&DevicePath, Fs);
+    if (EFI_ERROR (Status)) {
+      Print (L"Error: Cannot find a EFI system partition!\n");
+      return EFI_NOT_FOUND;
+    }
+  }
+
+  Print (L"Create Boot option for capsule on disk:\n");
+  Status = EfiBootManagerInitializeLoadOption (
+             &NewOption,
+             LoadOptionNumberUnassigned,
+             LoadOptionTypeBoot,
+             LOAD_OPTION_ACTIVE,
+             L"UEFI Capsule On Disk",
+             DevicePath,
+             (UINT8 *) &mCapsuleOnDiskBootOptionGuid,
+             sizeof(EFI_GUID)
+             );
+  if (!EFI_ERROR (Status)) {
+    Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1); {
+      if (!EFI_ERROR (Status)) {
+        *UpdateBootNext = TRUE;
+        *BootNext = (UINT16) NewOption.OptionNumber;
+        Print (L"  Boot%04x: %s\n", *BootNext, ConvertDevicePathToText(DevicePath, TRUE, TRUE));
+        return EFI_SUCCESS;
+      }
+    }
+  }
+
+  Print (L"ERROR: Cannot create boot option! - %r\n", Status);
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Write files to a given SimpleFileSystem.
+
+  @param[in] Buffer          The file buffer array
+  @param[in] BufferSize      The file buffer size array
+  @param[in] FileName        The file file name array
+  @param[in] BufferNum       The file buffer number
+  @param[in] Fs              The SimpleFileSystem handle to be written
+
+  @retval EFI_SUCCESS    Write file successfully
+  @retval EFI_NOT_FOUND  SFS protocol not found
+  @retval others         Write file failed
+
+**/
+EFI_STATUS
+WriteUpdateFile (
+  IN  VOID                                 **Buffer,
+  IN  UINTN                                *BufferSize,
+  IN  CHAR16                               **FileName,
+  IN  UINTN                                BufferNum,
+  IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL      *Fs
+)
+{
+  EFI_STATUS                          Status;
+  EFI_FILE                            *Root;
+  CHAR16                              *mDirName = L"\\EFI\\UpdateCapsule";
+  CHAR16                              *mDirName1 = L"\\EFI";
+  EFI_FILE_PROTOCOL                   *DirHandle = NULL;
+  EFI_FILE                            *FileHandle = NULL;
+  UINT64                              FileInfo;
+  UINTN                               Index = 0;
+  VOID                                *Filebuffer;
+  UINTN                               FileSize;
+
+  //
+  // Open Root from SFS
+  //
+  Status = Fs->OpenVolume (Fs, &Root);
+  if (EFI_ERROR (Status)) {
+    Print (L"Cannot open volume. Status = %r\n", Status);
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Ensure that efi and updatecapsule directories exist
+  //
+  Status = Root->Open (Root, &DirHandle, mDirName1, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0);
+  if (EFI_ERROR (Status)) {
+    Status = Root->Open (Root, &DirHandle, mDirName1, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY);
+    if (EFI_ERROR (Status)) {
+      Print(L"Unable to create %s directory\n", mDirName1);
+      return EFI_NOT_FOUND;
+    }
+  }
+  Status = Root->Open (Root, &DirHandle, mDirName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0);
+  if (EFI_ERROR (Status)) {
+    Status = Root->Open (Root, &DirHandle, mDirName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY);
+    if (EFI_ERROR (Status)) {
+      Print(L"Unable to create %s directory\n", mDirName);
+      return EFI_NOT_FOUND;
+    }
+  }
+
+  for (Index = 0; Index < BufferNum; Index ++) {
+    FileHandle = NULL;
+
+    //
+    // Open UpdateCapsule file
+    //
+    Status = DirHandle->Open (DirHandle, &FileHandle, FileName[Index], EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, 0);
+    if (EFI_ERROR (Status)) {
+      Print (L"Unable to create %s file\n", FileName[Index]);
+      return EFI_NOT_FOUND;
+    }
+
+    //
+    // Empty the file contents
+    //
+    Status = FileHandleGetSize (FileHandle, &FileInfo);
+    if (EFI_ERROR (Status)) {
+      FileHandleClose (FileHandle);
+      Print (L"Error Reading %s\n", FileName[Index]);
+      return EFI_DEVICE_ERROR;
+    }
+
+    //
+    // If the file size is already 0, then it has been empty.
+    //
+    if (FileInfo != 0) {
+      //
+      // Set the file size to 0.
+      //
+      FileInfo = 0;
+      Status = FileHandleSetSize (FileHandle, FileInfo);
+      if (EFI_ERROR (Status)) {
+        Print (L"Error Deleting %s\n", FileName[Index]);
+        FileHandleClose (FileHandle);
+        return Status;
+      }
+    }
+
+    //
+    // Write Filebuffer to file
+    //
+    Filebuffer = Buffer[Index];
+    FileSize = BufferSize[Index];
+    Status = FileHandleWrite (FileHandle, &FileSize, Filebuffer);
+    if (EFI_ERROR (Status)) {
+      Print (L"Unable to write Capsule Update to %s, Status = %r\n", FileName[Index], Status);
+      return EFI_NOT_FOUND;
+    }
+
+    Print (L"Succeed to write %s\n", FileName[Index]);
+    FileHandleClose (FileHandle);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Set capsule status variable.
+
+  @param[in] SetCap     Set or clear the capsule flag.
+
+  @retval EFI_SUCCESS   Succeed to set SetCap variable.
+  @retval others        Fail to set the variable.
+
+**/
+EFI_STATUS
+SetCapsuleStatusVariable (
+  BOOLEAN                       SetCap
+  )
+{
+  EFI_STATUS                    Status;
+  UINT64                        OsIndication;
+  UINTN                         DataSize;
+  
+  OsIndication = 0;
+  DataSize = sizeof(UINT64);
+  Status = gRT->GetVariable (
+                  L"OsIndications",
+                  &gEfiGlobalVariableGuid,
+                  NULL,
+                  &DataSize,
+                  &OsIndication
+                  );  
+  if (EFI_ERROR (Status)) {
+    OsIndication = 0;
+  }
+  if (SetCap) {
+    OsIndication |= ((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);
+  }
+  else {
+    OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);
+  }
+  Status = gRT->SetVariable (
+                  L"OsIndications",
+                  &gEfiGlobalVariableGuid,
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                  sizeof(UINT64),
+                  &OsIndication
+                  );
+
+  return Status;
+}
+
+/**
+  Process Capsule On Disk.
+
+  @param[in]  CapsuleBuffer    An array of pointer to capsule images
+  @param[in]  FileSize         An array of UINTN to capsule images size
+  @param[in]  FilePath         An array of capsule images file path
+  @param[in]  NewFileName      An array of new capsule images name
+  @param[in]  CapsuleNum       The count of capsule images
+
+  @retval EFI_SUCCESS       Capsule on disk secceed.
+  @retval others            Capsule on disk fail.
+
+**/
+EFI_STATUS
+ProcessCapsuleOnDisk (
+  IN VOID                          **CapsuleBuffer,
+  IN UINTN                         *CapsuleBufferSize,
+  IN CHAR16                        **FilePath,
+  IN CHAR16                        *Map,
+  IN UINTN                         CapsuleNum
+  )
+{
+  EFI_STATUS                      Status;
+  UINT16                          BootNext;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+  BOOLEAN                         UpdateBootNext;
+
+  //
+  // Get a valid file system from boot path
+  //
+  Fs = NULL;
+
+  Status = GetUpdateFileSystem (Map, &BootNext, &Fs, &UpdateBootNext);
+  if (EFI_ERROR (Status)) {
+    Print (L"CapsuleApp: cannot find a valid file system on boot devies. Status = %r\n", Status);
+    return Status;
+  }
+
+  //
+  // Copy capsule image to '\efi\UpdateCapsule\'
+  //
+  Status = WriteUpdateFile (CapsuleBuffer, CapsuleBufferSize, FilePath, CapsuleNum, Fs);
+  if (EFI_ERROR (Status)) {
+    Print (L"CapsuleApp: capsule image could not be copied for update.\n");
+    return Status;
+  }
+
+  //
+  // Set variable then reset
+  //
+  Status = SetCapsuleStatusVariable (TRUE);
+  if (EFI_ERROR (Status)) {
+    Print (L"CapsuleApp: unable to set OSIndication variable.\n");
+    return Status;
+  }
+
+  if (UpdateBootNext) {
+    Status = gRT->SetVariable (
+      L"BootNext",
+      &gEfiGlobalVariableGuid,
+      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      sizeof(UINT16),
+      &BootNext
+      );
+    if (EFI_ERROR (Status)){
+      Print (L"CapsuleApp: unable to set BootNext variable.\n");
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
\ No newline at end of file
diff --git a/MdeModulePkg/Include/Library/UefiBootManagerLib.h b/MdeModulePkg/Include/Library/UefiBootManagerLib.h
index bfc0cb86f8..9302398936 100644
--- a/MdeModulePkg/Include/Library/UefiBootManagerLib.h
+++ b/MdeModulePkg/Include/Library/UefiBootManagerLib.h
@@ -445,6 +445,25 @@ EfiBootManagerGetBootManagerMenu (
   EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
   );
 
+/**
+  Get the next possible full path pointing to the load option.
+  The routine doesn't guarantee the returned full path points to an existing
+  file, and it also doesn't guarantee the existing file is a valid load option.
+  BmGetNextLoadOptionBuffer() guarantees.
+
+  @param FilePath  The device path pointing to a load option.
+                   It could be a short-form device path.
+  @param FullPath  The full path returned by the routine in last call.
+                   Set to NULL in first call.
+
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EfiBootManagerGetNextFullDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FullPath
+  );
 
 /**
   Get the load option by its device path.
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
index 6a23477eb8..3b9195ba50 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
+++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
@@ -2461,3 +2461,25 @@ EfiBootManagerGetBootManagerMenu (
   }
 }
 
+/**
+  Get the next possible full path pointing to the load option.
+  The routine doesn't guarantee the returned full path points to an existing
+  file, and it also doesn't guarantee the existing file is a valid load option.
+  BmGetNextLoadOptionBuffer() guarantees.
+
+  @param FilePath  The device path pointing to a load option.
+                   It could be a short-form device path.
+  @param FullPath  The full path returned by the routine in last call.
+                   Set to NULL in first call.
+
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EfiBootManagerGetNextFullDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FullPath
+  )
+{
+  return BmGetNextLoadOptionDevicePath(FilePath, FullPath);
+}
-- 
2.16.2.windows.1



             reply	other threads:[~2019-01-28  1:21 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-28  1:21 Chen A Chen [this message]
2019-01-28  2:57 ` [PATCH v2 2/2] MdeModulePkg/CapsuleApp: Enhance CapsuleApp to support Capsule-on-Disk Wang, Jian J
2019-01-29  3:24 ` Wang, Jian J

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190128012115.11124-1-chen.a.chen@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox