]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg: Add Capsule On Disk APIs into CapsuleLib.
authorWei6 Xu <wei6.xu@intel.com>
Wed, 19 Jun 2019 16:55:40 +0000 (00:55 +0800)
committerZhang, Chao B <chao.b.zhang@intel.com>
Thu, 20 Jun 2019 11:49:30 +0000 (19:49 +0800)
https://github.com/tianocore/tianocore.github.io/wiki/UEFI-Capsule-
on-Disk-Introducation

CoDCheckCapsuleOnDiskFlag() is to check if CapsuleOnDisk flag in
"OsIndications" Variable is enabled. It is used to indicate whether
capsule on disk is provisioned in normal boot path.

CoDClearCapsuleOnDiskFlag() is to to clear CapsuleOnDisk flags,
including "OsIndications" and "BootNext" variable.

CoDRelocateCapsule() is to relocate the capsules from EFI system
partition. Depends on PcdCapsuleInRamSupport, there are two solutions
to relocate the capsule on disk images:
When Capsule In Ram is supported, the Capsule On Disk images are
relocated into memory, and call UpdateCapsule() service to deliver
the capsules.
When Capsule In Ram is not supported, the Capsule On Disk images are
relocated into a temp file which will be stored in root directory on
a platform specific storage device. CapsuleOnDiskLoadPei PEIM will
retrieve the capsules from the relocation temp file and report
capsule hobs for them.

CoDRemoveTempFile() is to remove the relocation temp file in the next
boot after capsules are processed.

Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Chao B Zhang <chao.b.zhang@intel.com>
Signed-off-by: Wei6 Xu <wei6.xu@intel.com>
Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
Reviewed-by: Chao B Zhang <chao.b.zhang@intel.com>
MdeModulePkg/Include/Library/CapsuleLib.h
MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c [new file with mode: 0644]
MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h [new file with mode: 0644]
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.c

index 1fc2fba3a2db3ee6019a6c1b30af0378426416cc..7a5414c80f19dbdd354e3a43f0ef3bf683ff7a82 100644 (file)
@@ -2,7 +2,7 @@
 \r
   This library class defines a set of interfaces for how to process capsule image updates.\r
 \r
-Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>\r
 SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
@@ -10,6 +10,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #ifndef __CAPSULE_LIB_H__\r
 #define __CAPSULE_LIB_H__\r
 \r
+//\r
+// BOOLEAN Variable to indicate whether system is in the capsule on disk state.\r
+//\r
+#define COD_RELOCATION_INFO_VAR_NAME   L"CodRelocationInfo"\r
+\r
 /**\r
   The firmware checks whether the capsule image is supported\r
   by the CapsuleGuid in CapsuleHeader or if there is other specific information in\r
@@ -81,4 +86,75 @@ ProcessCapsules (
   VOID\r
   );\r
 \r
+/**\r
+  This routine is called to check if CapsuleOnDisk flag in OsIndications Variable\r
+  is enabled.\r
+\r
+  @retval TRUE     Flag is enabled\r
+  @retval FALSE    Flag is not enabled\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+CoDCheckCapsuleOnDiskFlag(\r
+  VOID\r
+  );\r
+\r
+/**\r
+  This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable\r
+\r
+  @retval EFI_SUCCESS   All Capsule On Disk flags are cleared\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDClearCapsuleOnDiskFlag(\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Relocate Capsule on Disk from EFI system partition.\r
+\r
+  Two solution to deliver Capsule On Disk:\r
+  Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().\r
+  Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage\r
+  device with BlockIo protocol.\r
+\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  Side Effects:\r
+    Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.\r
+    Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file\r
+  systems of the relocation device will be corrupted.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+  @retval EFI_SUCCESS   Capsule on Disk images are successfully relocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRelocateCapsule(\r
+  UINTN     MaxRetry\r
+  );\r
+\r
+/**\r
+  Remove the temp file from the root of EFI System Partition.\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+  @retval EFI_SUCCESS   Remove the temp file successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRemoveTempFile (\r
+  UINTN    MaxRetry\r
+  );\r
+\r
 #endif\r
diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c
new file mode 100644 (file)
index 0000000..bb34e6d
--- /dev/null
@@ -0,0 +1,1966 @@
+/** @file\r
+  The implementation supports Capusle on Disk.\r
+\r
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "CapsuleOnDisk.h"\r
+\r
+/**\r
+  Return if this capsule is a capsule name capsule, based upon CapsuleHeader.\r
+\r
+  @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER\r
+\r
+  @retval TRUE  It is a capsule name capsule.\r
+  @retval FALSE It is not a capsule name capsule.\r
+**/\r
+BOOLEAN\r
+IsCapsuleNameCapsule (\r
+  IN EFI_CAPSULE_HEADER         *CapsuleHeader\r
+  );\r
+\r
+/**\r
+  Check the integrity of the capsule name capsule.\r
+  If the capsule is vaild, return the physical address of each capsule name string.\r
+\r
+  @param[in]  CapsuleHeader   Pointer to the capsule header of a capsule name capsule.\r
+  @param[out] CapsuleNameNum  Number of capsule name.\r
+\r
+  @retval NULL                Capsule name capsule is not valid.\r
+  @retval CapsuleNameBuf      Array of capsule name physical address.\r
+\r
+**/\r
+EFI_PHYSICAL_ADDRESS *\r
+ValidateCapsuleNameCapsuleIntegrity (\r
+  IN  EFI_CAPSULE_HEADER            *CapsuleHeader,\r
+  OUT UINTN                         *CapsuleNameNum\r
+  )\r
+{\r
+  UINT8                    *CapsuleNamePtr;\r
+  UINT8                    *CapsuleNameBufStart;\r
+  UINT8                    *CapsuleNameBufEnd;\r
+  UINTN                    Index;\r
+  UINTN                    StringSize;\r
+  EFI_PHYSICAL_ADDRESS     *CapsuleNameBuf;\r
+\r
+  if (!IsCapsuleNameCapsule (CapsuleHeader)) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Total string size must be even.\r
+  //\r
+  if (((CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize) & BIT0) != 0) {\r
+    return NULL;\r
+  }\r
+\r
+  *CapsuleNameNum = 0;\r
+  Index = 0;\r
+  CapsuleNameBufStart = (UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize;\r
+\r
+  //\r
+  // If strings are not aligned on a 16-bit boundary, reallocate memory for it.\r
+  //\r
+  if (((UINTN) CapsuleNameBufStart & BIT0) != 0) {\r
+    CapsuleNameBufStart = AllocateCopyPool (CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize, CapsuleNameBufStart);\r
+  }\r
+\r
+  CapsuleNameBufEnd = CapsuleNameBufStart + CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;\r
+\r
+  CapsuleNamePtr = CapsuleNameBufStart;\r
+  while (CapsuleNamePtr < CapsuleNameBufEnd) {\r
+    StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));\r
+    CapsuleNamePtr += StringSize;\r
+    (*CapsuleNameNum) ++;\r
+  }\r
+\r
+  //\r
+  // Integrity check.\r
+  //\r
+  if (CapsuleNamePtr != CapsuleNameBufEnd) {\r
+    if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {\r
+      FreePool (CapsuleNameBufStart);\r
+    }\r
+    return NULL;\r
+  }\r
+\r
+  CapsuleNameBuf = AllocatePool (*CapsuleNameNum * sizeof (EFI_PHYSICAL_ADDRESS));\r
+  if (CapsuleNameBuf == NULL) {\r
+    if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {\r
+      FreePool (CapsuleNameBufStart);\r
+    }\r
+    return NULL;\r
+  }\r
+\r
+  CapsuleNamePtr = CapsuleNameBufStart;\r
+  while (CapsuleNamePtr < CapsuleNameBufEnd) {\r
+    StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));\r
+    CapsuleNameBuf[Index] = (EFI_PHYSICAL_ADDRESS)(UINTN) CapsuleNamePtr;\r
+    CapsuleNamePtr += StringSize;\r
+    Index ++;\r
+  }\r
+\r
+  return CapsuleNameBuf;\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; 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 = StrnLenS(FileName, MAX_FILE_NAME_SIZE);\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_SIZE, &FileName[Index+1]);\r
+  }\r
+\r
+  //\r
+  // Copy First file name\r
+  //\r
+  StrnCpyS(FileNameFirst, MAX_FILE_NAME_SIZE, FileName, Index);\r
+  FileNameFirst[Index] = L'\0';\r
+}\r
+\r
+/**\r
+  This routine is called to get all boot options in the order determnined by:\r
+    1. "OptionBuf"\r
+    2. "BootOrder"\r
+\r
+  @param[out] OptionBuf           BootList buffer to all boot options returned\r
+  @param[out] OptionCount         BootList count of all boot options returned\r
+\r
+  @retval EFI_SUCCESS             There is no error when processing capsule\r
+\r
+**/\r
+EFI_STATUS\r
+GetBootOptionInOrder(\r
+  OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf,\r
+  OUT UINTN                        *OptionCount\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  UINTN                        DataSize;\r
+  UINT16                       BootNext;\r
+  CHAR16                       BootOptionName[20];\r
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOrderOptionBuf;\r
+  UINTN                        BootOrderCount;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry;\r
+  UINTN                        BootNextCount;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION *TempBuf;\r
+\r
+  BootOrderOptionBuf  = NULL;\r
+  TempBuf             = NULL;\r
+  BootNextCount       = 0;\r
+  BootOrderCount      = 0;\r
+  *OptionBuf          = NULL;\r
+  *OptionCount        = 0;\r
+\r
+  //\r
+  // First Get BootOption from "BootNext"\r
+  //\r
+  DataSize = sizeof(BootNext);\r
+  Status = gRT->GetVariable (\r
+                  EFI_BOOT_NEXT_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  NULL,\r
+                  &DataSize,\r
+                  (VOID *)&BootNext\r
+                  );\r
+  //\r
+  // BootNext variable is a single UINT16\r
+  //\r
+  if (!EFI_ERROR(Status) && DataSize == sizeof(UINT16)) {\r
+    //\r
+    // Add the boot next boot option\r
+    //\r
+    UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootNext);\r
+    ZeroMem(&BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));\r
+    Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry);\r
+\r
+    if (!EFI_ERROR(Status)) {\r
+      BootNextCount = 1;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Second get BootOption from "BootOrder"\r
+  //\r
+  BootOrderOptionBuf = EfiBootManagerGetLoadOptions (&BootOrderCount, LoadOptionTypeBoot);\r
+  if (BootNextCount == 0 && BootOrderCount == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // At least one BootOption is found\r
+  //\r
+  TempBuf = AllocatePool(sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * (BootNextCount + BootOrderCount));\r
+  if (TempBuf != NULL) {\r
+    if (BootNextCount == 1) {\r
+      CopyMem(TempBuf, &BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));\r
+    }\r
+\r
+    if (BootOrderCount > 0) {\r
+      CopyMem(TempBuf + BootNextCount, BootOrderOptionBuf, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * BootOrderCount);\r
+    }\r
+\r
+    *OptionBuf   = TempBuf;\r
+    *OptionCount = BootNextCount + BootOrderCount;\r
+    Status = EFI_SUCCESS;\r
+  } else {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  FreePool(BootOrderOptionBuf);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This routine is called to get boot option by OptionNumber.\r
+\r
+  @param[in] Number               The OptionNumber of boot option\r
+  @param[out] OptionBuf           BootList buffer to all boot options returned\r
+\r
+  @retval EFI_SUCCESS             There is no error when getting boot option\r
+\r
+**/\r
+EFI_STATUS\r
+GetBootOptionByNumber(\r
+  IN  UINT16                       Number,\r
+  OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  CHAR16                        BootOptionName[20];\r
+  EFI_BOOT_MANAGER_LOAD_OPTION  BootOption;\r
+\r
+  UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", Number);\r
+  ZeroMem (&BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+  Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootOption);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    *OptionBuf = AllocatePool (sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+    CopyMem (*OptionBuf, &BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Get Active EFI System Partition within GPT based on device path.\r
+\r
+  @param[in] DevicePath    Device path to find a active EFI System Partition\r
+  @param[out] FsHandle     BootList points to all boot options returned\r
+\r
+  @retval EFI_SUCCESS      Active EFI System Partition is succesfully found\r
+  @retval EFI_NOT_FOUND    No Active EFI System Partition is found\r
+\r
+**/\r
+EFI_STATUS\r
+GetEfiSysPartitionFromDevPath(\r
+  IN EFI_DEVICE_PATH_PROTOCOL         *DevicePath,\r
+  OUT EFI_HANDLE                      *FsHandle\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_DEVICE_PATH_PROTOCOL        *TempDevicePath;\r
+  HARDDRIVE_DEVICE_PATH           *Hd;\r
+  EFI_HANDLE                      Handle;\r
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+\r
+  //\r
+  // Check if the device path contains GPT node\r
+  //\r
+  TempDevicePath = DevicePath;\r
+  while (!IsDevicePathEnd (TempDevicePath)) {\r
+    if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&\r
+       (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {\r
+      Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;\r
+      if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {\r
+        break;\r
+      }\r
+    }\r
+    TempDevicePath = NextDevicePathNode (TempDevicePath);\r
+  }\r
+\r
+  if (!IsDevicePathEnd (TempDevicePath)) {\r
+    //\r
+    // Search for EFI system partition protocol on full device path in Boot Option\r
+    //\r
+    Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);\r
+\r
+    //\r
+    // Search for simple file system on this handler\r
+    //\r
+    if (!EFI_ERROR(Status)) {\r
+      Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+      if (!EFI_ERROR(Status)) {\r
+        *FsHandle = Handle;\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  This routine is called to get Simple File System protocol on the first EFI system partition found in\r
+  active boot option. The boot option list is detemined in order by\r
+    1. "BootNext"\r
+    2. "BootOrder"\r
+\r
+  @param[in]       MaxRetry           Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                      device like USB can get enumerated.\r
+  @param[in, out]  LoadOptionNumber   On input, specify the boot option to get EFI system partition.\r
+                                      On output, return the OptionNumber of the boot option where EFI\r
+                                      system partition is got from.\r
+  @param[out]      FsFsHandle         Simple File System Protocol found on first active EFI system partition\r
+\r
+  @retval EFI_SUCCESS     Simple File System protocol found for EFI system partition\r
+  @retval EFI_NOT_FOUND   No Simple File System protocol found for EFI system partition\r
+\r
+**/\r
+EFI_STATUS\r
+GetEfiSysPartitionFromActiveBootOption(\r
+  IN UINTN                             MaxRetry,\r
+  IN OUT UINT16                        **LoadOptionNumber,\r
+  OUT EFI_HANDLE                       *FsHandle\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuf;\r
+  UINTN                        BootOptionNum;\r
+  UINTN                        Index;\r
+  EFI_DEVICE_PATH_PROTOCOL     *DevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL     *CurFullPath;\r
+  EFI_DEVICE_PATH_PROTOCOL     *PreFullPath;\r
+\r
+  *FsHandle = NULL;\r
+\r
+  if (*LoadOptionNumber != NULL) {\r
+    BootOptionNum = 1;\r
+    Status = GetBootOptionByNumber(**LoadOptionNumber, &BootOptionBuf);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status));\r
+      return Status;\r
+    }\r
+  } else {\r
+    Status = GetBootOptionInOrder(&BootOptionBuf, &BootOptionNum);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status));\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Search BootOptionList to check if it is an active boot option with EFI system partition\r
+  //  1. Connect device path\r
+  //  2. expend short/plug in devicepath\r
+  //  3. LoadImage\r
+  //\r
+  for (Index = 0; Index < BootOptionNum; Index++) {\r
+    //\r
+    // Get the boot option from the link list\r
+    //\r
+    DevicePath  = BootOptionBuf[Index].FilePath;\r
+\r
+    //\r
+    // Skip inactive or legacy boot options\r
+    //\r
+    if ((BootOptionBuf[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 ||\r
+        DevicePathType (DevicePath) == BBS_DEVICE_PATH) {\r
+      continue;\r
+    }\r
+\r
+    DEBUG_CODE (\r
+      CHAR16 *DevicePathStr;\r
+\r
+      DevicePathStr = ConvertDevicePathToText(DevicePath, TRUE, TRUE);\r
+      if (DevicePathStr != NULL){\r
+        DEBUG((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr));\r
+        FreePool(DevicePathStr);\r
+      } else {\r
+        DEBUG((DEBUG_INFO, "DevicePathToStr failed\n"));\r
+      }\r
+    );\r
+\r
+    CurFullPath = NULL;\r
+    //\r
+    // Try every full device Path generated from bootoption\r
+    //\r
+    do {\r
+      PreFullPath = CurFullPath;\r
+      CurFullPath = EfiBootManagerGetNextLoadOptionDevicePath(DevicePath, CurFullPath);\r
+\r
+      if (PreFullPath != NULL) {\r
+        FreePool (PreFullPath);\r
+      }\r
+\r
+      if (CurFullPath == NULL) {\r
+        //\r
+        // No Active EFI system partition is found in BootOption device path\r
+        //\r
+        Status = EFI_NOT_FOUND;\r
+        break;\r
+      }\r
+\r
+      DEBUG_CODE (\r
+        CHAR16 *DevicePathStr1;\r
+\r
+        DevicePathStr1 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);\r
+        if (DevicePathStr1 != NULL){\r
+          DEBUG((DEBUG_INFO, "Full device path %s\n", DevicePathStr1));\r
+          FreePool(DevicePathStr1);\r
+        }\r
+      );\r
+\r
+      //\r
+      // Make sure the boot option device path connected.\r
+      // Only handle first device in boot option. Other optional device paths are described as OSV specific\r
+      // FullDevice could contain extra directory & file info. So don't check connection status here.\r
+      //\r
+      EfiBootManagerConnectDevicePath (CurFullPath, NULL);\r
+      Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);\r
+\r
+      //\r
+      // Some relocation device like USB need more time to get enumerated\r
+      //\r
+      while (EFI_ERROR(Status) && MaxRetry > 0) {\r
+        EfiBootManagerConnectDevicePath(CurFullPath, NULL);\r
+\r
+        //\r
+        // Search for EFI system partition protocol on full device path in Boot Option\r
+        //\r
+        Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);\r
+        if (!EFI_ERROR(Status)) {\r
+          break;\r
+        }\r
+        DEBUG((DEBUG_ERROR, "GetEfiSysPartitionFromDevPath Loop %x\n", Status));\r
+        //\r
+        // Stall 100ms if connection failed to ensure USB stack is ready\r
+        //\r
+        gBS->Stall(100000);\r
+        MaxRetry --;\r
+      }\r
+    } while(EFI_ERROR(Status));\r
+\r
+    //\r
+    // Find a qualified Simple File System\r
+    //\r
+    if (!EFI_ERROR(Status)) {\r
+      break;\r
+    }\r
+\r
+  }\r
+\r
+  //\r
+  // Return the OptionNumber of the boot option where EFI system partition is got from\r
+  //\r
+  if (*LoadOptionNumber == NULL) {\r
+    *LoadOptionNumber = AllocateCopyPool (sizeof(UINT16), (UINT16 *) &BootOptionBuf[Index].OptionNumber);\r
+  }\r
+\r
+  //\r
+  // No qualified EFI system partition found\r
+  //\r
+  if (*FsHandle == NULL) {\r
+    Status = EFI_NOT_FOUND;\r
+  }\r
+\r
+  DEBUG_CODE (\r
+    CHAR16 *DevicePathStr2;\r
+    if (*FsHandle != NULL) {\r
+      DevicePathStr2 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);\r
+      if (DevicePathStr2 != NULL){\r
+        DEBUG((DEBUG_INFO, "Found Active EFI System Partion on %s\n", DevicePathStr2));\r
+        FreePool(DevicePathStr2);\r
+      }\r
+    } else {\r
+      DEBUG((DEBUG_INFO, "Failed to found Active EFI System Partion\n"));\r
+    }\r
+  );\r
+\r
+  if (CurFullPath != NULL) {\r
+    FreePool(CurFullPath);\r
+  }\r
+\r
+  //\r
+  // Free BootOption Buffer\r
+  //\r
+  for (Index = 0; Index < BootOptionNum; Index++) {\r
+    if (BootOptionBuf[Index].Description != NULL) {\r
+      FreePool(BootOptionBuf[Index].Description);\r
+    }\r
+\r
+    if (BootOptionBuf[Index].FilePath != NULL) {\r
+      FreePool(BootOptionBuf[Index].FilePath);\r
+    }\r
+\r
+    if (BootOptionBuf[Index].OptionalData != NULL) {\r
+      FreePool(BootOptionBuf[Index].OptionalData);\r
+    }\r
+  }\r
+\r
+  FreePool(BootOptionBuf);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in\r
+  alphabetical order described in UEFI spec.\r
+\r
+  @param[in]  Dir                 Directory file handler\r
+  @param[in]  FileAttr            Attribute of file to be red from directory\r
+  @param[out] FileInfoList        File images info list red from directory\r
+  @param[out] FileNum             File images number red from directory\r
+\r
+  @retval EFI_SUCCESS             File FileInfo list in the given\r
+\r
+**/\r
+EFI_STATUS\r
+GetFileInfoListInAlphabetFromDir(\r
+  IN EFI_FILE_HANDLE  Dir,\r
+  IN UINT64           FileAttr,\r
+  OUT LIST_ENTRY      *FileInfoList,\r
+  OUT UINTN           *FileNum\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  FILE_INFO_ENTRY   *NewFileInfoEntry;\r
+  FILE_INFO_ENTRY   *TempFileInfoEntry;\r
+  EFI_FILE_INFO     *FileInfo;\r
+  CHAR16            *NewFileName;\r
+  CHAR16            *ListedFileName;\r
+  CHAR16            *NewFileNameExtension;\r
+  CHAR16            *ListedFileNameExtension;\r
+  CHAR16            *TempNewSubStr;\r
+  CHAR16            *TempListedSubStr;\r
+  LIST_ENTRY        *Link;\r
+  BOOLEAN           NoFile;\r
+  UINTN             FileCount;\r
+  UINTN             IndexNew;\r
+  UINTN             IndexListed;\r
+  UINTN             NewSubStrLen;\r
+  UINTN             ListedSubStrLen;\r
+  INTN              SubStrCmpResult;\r
+\r
+  Status                  = EFI_SUCCESS;\r
+  NewFileName             = NULL;\r
+  ListedFileName          = NULL;\r
+  NewFileNameExtension    = NULL;\r
+  ListedFileNameExtension = NULL;\r
+  TempNewSubStr           = NULL;\r
+  TempListedSubStr        = NULL;\r
+  NoFile                  = FALSE;\r
+  FileCount               = 0;\r
+\r
+  InitializeListHead(FileInfoList);\r
+\r
+  TempNewSubStr           = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+  TempListedSubStr        = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+\r
+  if (TempNewSubStr == NULL || TempListedSubStr == NULL ) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto EXIT;\r
+  }\r
+\r
+  for ( Status = FileHandleFindFirstFile(Dir, &FileInfo)\r
+      ; !EFI_ERROR(Status) && !NoFile\r
+      ; Status = FileHandleFindNextFile(Dir, FileInfo, &NoFile)\r
+     ){\r
+\r
+    //\r
+    // Skip file with mismatching File attribute\r
+    //\r
+    if ((FileInfo->Attribute & (FileAttr)) == 0) {\r
+      continue;\r
+    }\r
+\r
+    NewFileInfoEntry = NULL;\r
+    NewFileInfoEntry = (FILE_INFO_ENTRY*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY));\r
+    if (NewFileInfoEntry == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto EXIT;\r
+    }\r
+    NewFileInfoEntry->Signature = FILE_INFO_SIGNATURE;\r
+    NewFileInfoEntry->FileInfo  = AllocateCopyPool((UINTN) FileInfo->Size, FileInfo);\r
+    if (NewFileInfoEntry->FileInfo == NULL) {\r
+      FreePool(NewFileInfoEntry);\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto EXIT;\r
+    }\r
+\r
+    NewFileInfoEntry->FileNameFirstPart  = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+    if (NewFileInfoEntry->FileNameFirstPart == NULL) {\r
+      FreePool(NewFileInfoEntry->FileInfo);\r
+      FreePool(NewFileInfoEntry);\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto EXIT;\r
+    }\r
+    NewFileInfoEntry->FileNameSecondPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+    if (NewFileInfoEntry->FileNameSecondPart == NULL) {\r
+      FreePool(NewFileInfoEntry->FileInfo);\r
+      FreePool(NewFileInfoEntry->FileNameFirstPart);\r
+      FreePool(NewFileInfoEntry);\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto EXIT;\r
+    }\r
+\r
+    //\r
+    // Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension\r
+    // If no period in the whole file name. NewFileExtension is set to L'\0'\r
+    //\r
+    NewFileName          = NewFileInfoEntry->FileNameFirstPart;\r
+    NewFileNameExtension = NewFileInfoEntry->FileNameSecondPart;\r
+    SplitFileNameExtension(FileInfo->FileName, NewFileName, NewFileNameExtension);\r
+    UpperCaseString(NewFileName);\r
+    UpperCaseString(NewFileNameExtension);\r
+\r
+    //\r
+    // Insert capsule file in alphabetical ordered list\r
+    //\r
+    for (Link = FileInfoList->ForwardLink; Link != FileInfoList; Link = Link->ForwardLink) {\r
+      //\r
+      // Get the FileInfo from the link list\r
+      //\r
+      TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+      ListedFileName          = TempFileInfoEntry->FileNameFirstPart;\r
+      ListedFileNameExtension = TempFileInfoEntry->FileNameSecondPart;\r
+\r
+      //\r
+      // Follow rule in UEFI spec 8.5.5 to compare file name\r
+      //\r
+      IndexListed = 0;\r
+      IndexNew    = 0;\r
+      while (TRUE){\r
+        //\r
+        // First compare each substrings in NewFileName & ListedFileName between periods\r
+        //\r
+        GetSubStringBeforePeriod(&NewFileName[IndexNew], TempNewSubStr, &NewSubStrLen);\r
+        GetSubStringBeforePeriod(&ListedFileName[IndexListed], TempListedSubStr, &ListedSubStrLen);\r
+        if (NewSubStrLen > ListedSubStrLen) {\r
+          //\r
+          // Substr in NewFileName is longer. Pad tail with SPACE\r
+          //\r
+          PadStrInTail(TempListedSubStr, NewSubStrLen - ListedSubStrLen, L' ');\r
+        } else if (NewSubStrLen < ListedSubStrLen){\r
+          //\r
+          // Substr in ListedFileName is longer. Pad tail with SPACE\r
+          //\r
+          PadStrInTail(TempNewSubStr, ListedSubStrLen - NewSubStrLen, L' ');\r
+        }\r
+\r
+        SubStrCmpResult = StrnCmp(TempNewSubStr, TempListedSubStr, MAX_FILE_NAME_LEN);\r
+        if (SubStrCmpResult != 0) {\r
+          break;\r
+        }\r
+\r
+        //\r
+        // Move to skip this substring\r
+        //\r
+        IndexNew    += NewSubStrLen;\r
+        IndexListed += ListedSubStrLen;\r
+        //\r
+        // Reach File First Name end\r
+        //\r
+        if (NewFileName[IndexNew] == L'\0' || ListedFileName[IndexListed] == L'\0') {\r
+          break;\r
+        }\r
+\r
+        //\r
+        // Skip the period L'.'\r
+        //\r
+        IndexNew++;\r
+        IndexListed++;\r
+      }\r
+\r
+      if (SubStrCmpResult < 0) {\r
+        //\r
+        // NewFileName is smaller. Find the right place to insert New file\r
+        //\r
+        break;\r
+      } else if (SubStrCmpResult == 0) {\r
+        //\r
+        // 2 cases whole NewFileName is smaller than ListedFileName\r
+        //   1. if NewFileName == ListedFileName. Continue to compare FileNameExtension\r
+        //   2. if NewFileName is shorter than ListedFileName\r
+        //\r
+        if (NewFileName[IndexNew] == L'\0') {\r
+          if (ListedFileName[IndexListed] != L'\0' || (StrnCmp(NewFileNameExtension, ListedFileNameExtension, MAX_FILE_NAME_LEN) < 0)) {\r
+            break;\r
+          }\r
+        }\r
+      }\r
+\r
+      //\r
+      // Other case, ListedFileName is smaller. Continue to compare the next file in the list\r
+      //\r
+    }\r
+\r
+    //\r
+    // If Find an entry in the list whose name is bigger than new FileInfo in alphabet order\r
+    //    Insert it before this entry\r
+    // else\r
+    //    Insert at the tail of this list (Link = FileInfoList)\r
+    //\r
+    InsertTailList(Link, &NewFileInfoEntry->Link);\r
+\r
+    FileCount++;\r
+  }\r
+\r
+  *FileNum = FileCount;\r
+\r
+EXIT:\r
+\r
+  if (TempNewSubStr != NULL) {\r
+    FreePool(TempNewSubStr);\r
+  }\r
+\r
+  if (TempListedSubStr != NULL) {\r
+    FreePool(TempListedSubStr);\r
+  }\r
+\r
+  if (EFI_ERROR(Status)) {\r
+    while(!IsListEmpty(FileInfoList)) {\r
+      Link = FileInfoList->ForwardLink;\r
+      RemoveEntryList(Link);\r
+\r
+      TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+\r
+      FreePool(TempFileInfoEntry->FileInfo);\r
+      FreePool(TempFileInfoEntry->FileNameFirstPart);\r
+      FreePool(TempFileInfoEntry->FileNameSecondPart);\r
+      FreePool(TempFileInfoEntry);\r
+    }\r
+    *FileNum = 0;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This routine is called to get all qualified image from file from an given directory\r
+  in alphabetic order. All the file image is copied to allocated boottime memory.\r
+  Caller should free these memory\r
+\r
+  @param[in]  Dir            Directory file handler\r
+  @param[in]  FileAttr       Attribute of file to be red from directory\r
+  @param[out] FilePtr        File images Info buffer red from directory\r
+  @param[out] FileNum        File images number red from directory\r
+\r
+  @retval EFI_SUCCESS  Succeed to get all capsules in alphabetic order.\r
+\r
+**/\r
+EFI_STATUS\r
+GetFileImageInAlphabetFromDir(\r
+  IN EFI_FILE_HANDLE   Dir,\r
+  IN UINT64            FileAttr,\r
+  OUT IMAGE_INFO       **FilePtr,\r
+  OUT UINTN            *FileNum\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  LIST_ENTRY            *Link;\r
+  EFI_FILE_HANDLE       FileHandle;\r
+  FILE_INFO_ENTRY       *FileInfoEntry;\r
+  EFI_FILE_INFO         *FileInfo;\r
+  UINTN                 FileCount;\r
+  IMAGE_INFO            *TempFilePtrBuf;\r
+  UINTN                 Size;\r
+  LIST_ENTRY            FileInfoList;\r
+\r
+  FileHandle       = NULL;\r
+  FileCount        = 0;\r
+  TempFilePtrBuf   = NULL;\r
+  *FilePtr         = NULL;\r
+\r
+  //\r
+  // Get file list in Dir in alphabetical order\r
+  //\r
+  Status = GetFileInfoListInAlphabetFromDir(\r
+             Dir,\r
+             FileAttr,\r
+             &FileInfoList,\r
+             &FileCount\r
+             );\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));\r
+    goto EXIT;\r
+  }\r
+\r
+  if (FileCount == 0) {\r
+    DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));\r
+    Status = EFI_NOT_FOUND;\r
+    goto EXIT;\r
+  }\r
+\r
+  TempFilePtrBuf = (IMAGE_INFO *)AllocateZeroPool(sizeof(IMAGE_INFO) * FileCount);\r
+  if (TempFilePtrBuf == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Read all files from FileInfoList to BS memory\r
+  //\r
+  FileCount = 0;\r
+  for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {\r
+    //\r
+    // Get FileInfo from the link list\r
+    //\r
+    FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+    FileInfo      = FileInfoEntry->FileInfo;\r
+\r
+    Status = Dir->Open(\r
+                    Dir,\r
+                    &FileHandle,\r
+                    FileInfo->FileName,\r
+                    EFI_FILE_MODE_READ,\r
+                    0\r
+                    );\r
+    if (EFI_ERROR(Status)){\r
+      continue;\r
+    }\r
+\r
+    Size = (UINTN)FileInfo->FileSize;\r
+    TempFilePtrBuf[FileCount].ImageAddress = AllocateZeroPool(Size);\r
+    if (TempFilePtrBuf[FileCount].ImageAddress == NULL) {\r
+      DEBUG((DEBUG_ERROR, "Fail to allocate memory for capsule. Stop processing the rest.\n"));\r
+      break;\r
+    }\r
+\r
+    Status = FileHandle->Read(\r
+                           FileHandle,\r
+                           &Size,\r
+                           TempFilePtrBuf[FileCount].ImageAddress\r
+                           );\r
+\r
+    FileHandle->Close(FileHandle);\r
+\r
+    //\r
+    // Skip read error file\r
+    //\r
+    if (EFI_ERROR(Status) || Size != (UINTN)FileInfo->FileSize) {\r
+      //\r
+      // Remove this error file info accordingly\r
+      // & move Link to BackLink\r
+      //\r
+      Link = RemoveEntryList(Link);\r
+      Link = Link->BackLink;\r
+\r
+      FreePool(FileInfoEntry->FileInfo);\r
+      FreePool(FileInfoEntry->FileNameFirstPart);\r
+      FreePool(FileInfoEntry->FileNameSecondPart);\r
+      FreePool(FileInfoEntry);\r
+\r
+      FreePool(TempFilePtrBuf[FileCount].ImageAddress);\r
+      TempFilePtrBuf[FileCount].ImageAddress = NULL;\r
+      TempFilePtrBuf[FileCount].FileInfo     = NULL;\r
+\r
+      continue;\r
+    }\r
+    TempFilePtrBuf[FileCount].FileInfo = FileInfo;\r
+    FileCount++;\r
+  }\r
+\r
+  DEBUG_CODE (\r
+    for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {\r
+      FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+      FileInfo      = FileInfoEntry->FileInfo;\r
+      DEBUG((DEBUG_INFO, "Successfully read capsule file %s from disk.\n", FileInfo->FileName));\r
+    }\r
+  );\r
+\r
+EXIT:\r
+\r
+  *FilePtr = TempFilePtrBuf;\r
+  *FileNum = FileCount;\r
+\r
+  //\r
+  // FileInfo will be freed by Calller\r
+  //\r
+  while(!IsListEmpty(&FileInfoList)) {\r
+    Link = FileInfoList.ForwardLink;\r
+    RemoveEntryList(Link);\r
+\r
+    FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+\r
+    FreePool(FileInfoEntry->FileNameFirstPart);\r
+    FreePool(FileInfoEntry->FileNameSecondPart);\r
+    FreePool(FileInfoEntry);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This routine is called to remove all qualified image from file from an given directory.\r
+\r
+  @param[in] Dir                  Directory file handler\r
+  @param[in] FileAttr             Attribute of files to be deleted\r
+\r
+  @retval EFI_SUCCESS  Succeed to remove all files from an given directory.\r
+\r
+**/\r
+EFI_STATUS\r
+RemoveFileFromDir(\r
+  IN EFI_FILE_HANDLE   Dir,\r
+  IN UINT64            FileAttr\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  LIST_ENTRY        *Link;\r
+  LIST_ENTRY        FileInfoList;\r
+  EFI_FILE_HANDLE   FileHandle;\r
+  FILE_INFO_ENTRY   *FileInfoEntry;\r
+  EFI_FILE_INFO     *FileInfo;\r
+  UINTN             FileCount;\r
+\r
+  FileHandle = NULL;\r
+\r
+  //\r
+  // Get file list in Dir in alphabetical order\r
+  //\r
+  Status = GetFileInfoListInAlphabetFromDir(\r
+             Dir,\r
+             FileAttr,\r
+             &FileInfoList,\r
+             &FileCount\r
+             );\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));\r
+    goto EXIT;\r
+  }\r
+\r
+  if (FileCount == 0) {\r
+    DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));\r
+    Status = EFI_NOT_FOUND;\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Delete all files with given attribute in Dir\r
+  //\r
+  for (Link = FileInfoList.ForwardLink; Link != &(FileInfoList); Link = Link->ForwardLink) {\r
+    //\r
+    // Get FileInfo from the link list\r
+    //\r
+    FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+    FileInfo      = FileInfoEntry->FileInfo;\r
+\r
+    Status = Dir->Open(\r
+                    Dir,\r
+                    &FileHandle,\r
+                    FileInfo->FileName,\r
+                    EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
+                    0\r
+                    );\r
+    if (EFI_ERROR(Status)){\r
+      continue;\r
+    }\r
+\r
+    Status = FileHandle->Delete(FileHandle);\r
+  }\r
+\r
+EXIT:\r
+\r
+  while(!IsListEmpty(&FileInfoList)) {\r
+    Link = FileInfoList.ForwardLink;\r
+    RemoveEntryList(Link);\r
+\r
+    FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+\r
+    FreePool(FileInfoEntry->FileInfo);\r
+    FreePool(FileInfoEntry);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This routine is called to get all caspules from file. The capsule file image is\r
+  copied to BS memory. Caller is responsible to free them.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated.\r
+  @param[out]   CapsulePtr           Copied Capsule file Image Info buffer\r
+  @param[out]   CapsuleNum           CapsuleNumber\r
+  @param[out]   FsHandle             File system handle\r
+  @param[out]   LoadOptionNumber     OptionNumber of boot option\r
+\r
+  @retval EFI_SUCCESS  Succeed to get all capsules.\r
+\r
+**/\r
+EFI_STATUS\r
+GetAllCapsuleOnDisk(\r
+  IN  UINTN                            MaxRetry,\r
+  OUT IMAGE_INFO                       **CapsulePtr,\r
+  OUT UINTN                            *CapsuleNum,\r
+  OUT EFI_HANDLE                       *FsHandle,\r
+  OUT UINT16                            *LoadOptionNumber\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *Fs;\r
+  EFI_FILE_HANDLE                  RootDir;\r
+  EFI_FILE_HANDLE                  FileDir;\r
+  UINT16                           *TempOptionNumber;\r
+\r
+  Fs               = NULL;\r
+  RootDir          = NULL;\r
+  FileDir          = NULL;\r
+  TempOptionNumber = NULL;\r
+  *CapsuleNum      = 0;\r
+\r
+  Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &TempOptionNumber, FsHandle);\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol(*FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = Fs->OpenVolume(Fs, &RootDir);\r
+  if (EFI_ERROR(Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = RootDir->Open(\r
+                      RootDir,\r
+                      &FileDir,\r
+                      EFI_CAPSULE_FILE_DIRECTORY,\r
+                      EFI_FILE_MODE_READ,\r
+                      0\r
+                      );\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG((DEBUG_ERROR, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute\r
+  // ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY\r
+  //\r
+  Status = GetFileImageInAlphabetFromDir(\r
+             FileDir,\r
+             EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE,\r
+             CapsulePtr,\r
+             CapsuleNum\r
+             );\r
+  DEBUG((DEBUG_INFO, "GetFileImageInAlphabetFromDir status %x\n", Status));\r
+\r
+  //\r
+  // Always remove file to avoid deadloop in capsule process\r
+  //\r
+  Status = RemoveFileFromDir(FileDir, EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE);\r
+  DEBUG((DEBUG_INFO, "RemoveFileFromDir status %x\n", Status));\r
+\r
+  if (LoadOptionNumber != NULL) {\r
+    *LoadOptionNumber = *TempOptionNumber;\r
+  }\r
+\r
+EXIT:\r
+\r
+  if (FileDir != NULL) {\r
+    FileDir->Close (FileDir);\r
+  }\r
+\r
+  if (RootDir != NULL) {\r
+    RootDir->Close (RootDir);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Build Gather list for a list of capsule images.\r
+\r
+  @param[in]  CapsuleBuffer    An array of pointer to capsule images\r
+  @param[in]  CapsuleSize      An array of UINTN to capsule images size\r
+  @param[in]  CapsuleNum       The count of capsule images\r
+  @param[out] BlockDescriptors The block descriptors for the capsule images\r
+\r
+  @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.\r
+\r
+**/\r
+EFI_STATUS\r
+BuildGatherList (\r
+  IN VOID                          **CapsuleBuffer,\r
+  IN UINTN                         *CapsuleSize,\r
+  IN UINTN                         CapsuleNum,\r
+  OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR  *BlockDescriptors1;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR  *BlockDescriptorPre;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR  *BlockDescriptorsHeader;\r
+  UINTN                         Index;\r
+\r
+  BlockDescriptors1      = NULL;\r
+  BlockDescriptorPre     = NULL;\r
+  BlockDescriptorsHeader = NULL;\r
+\r
+  for (Index = 0; Index < CapsuleNum; Index++) {\r
+    //\r
+    // Allocate memory for the descriptors.\r
+    //\r
+    BlockDescriptors1  = AllocateZeroPool (2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR));\r
+    if (BlockDescriptors1 == NULL) {\r
+      DEBUG ((DEBUG_ERROR, "BuildGatherList: failed to allocate memory for descriptors\n"));\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ERREXIT;\r
+    } else {\r
+      DEBUG ((DEBUG_INFO, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1));\r
+    }\r
+\r
+    //\r
+    // Record descirptor header\r
+    //\r
+    if (Index == 0) {\r
+      BlockDescriptorsHeader = BlockDescriptors1;\r
+    }\r
+\r
+    if (BlockDescriptorPre != NULL) {\r
+      BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;\r
+      BlockDescriptorPre->Length = 0;\r
+    }\r
+\r
+    BlockDescriptors1->Union.DataBlock = (UINTN) CapsuleBuffer[Index];\r
+    BlockDescriptors1->Length = CapsuleSize[Index];\r
+\r
+    BlockDescriptorPre = BlockDescriptors1 + 1;\r
+    BlockDescriptors1 = NULL;\r
+  }\r
+\r
+  //\r
+  // Null-terminate.\r
+  //\r
+  if (BlockDescriptorPre != NULL) {\r
+    BlockDescriptorPre->Union.ContinuationPointer = (UINTN)NULL;\r
+    BlockDescriptorPre->Length = 0;\r
+    *BlockDescriptors = BlockDescriptorsHeader;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ERREXIT:\r
+  if (BlockDescriptors1 != NULL) {\r
+    FreePool (BlockDescriptors1);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This routine is called to check if CapsuleOnDisk flag in OsIndications Variable\r
+  is enabled.\r
+\r
+  @retval TRUE     Flag is enabled\r
+  @retval FALSE    Flag is not enabled\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+CoDCheckCapsuleOnDiskFlag(\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINT64                OsIndication;\r
+  UINTN                 DataSize;\r
+\r
+  //\r
+  // Check File Capsule Delivery Supported Flag in OsIndication variable\r
+  //\r
+  OsIndication = 0;\r
+  DataSize     = sizeof(UINT64);\r
+  Status = gRT->GetVariable (\r
+                  EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  NULL,\r
+                  &DataSize,\r
+                  &OsIndication\r
+                  );\r
+  if (!EFI_ERROR(Status) &&\r
+      (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+\r
+/**\r
+  This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.\r
+\r
+  @retval EFI_SUCCESS   All Capsule On Disk flags are cleared\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDClearCapsuleOnDiskFlag(\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINT64                OsIndication;\r
+  UINTN                 DataSize;\r
+\r
+  //\r
+  // Reset File Capsule Delivery Supported Flag in OsIndication variable\r
+  //\r
+  OsIndication = 0;\r
+  DataSize = sizeof(UINT64);\r
+  Status = gRT->GetVariable (\r
+                  EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  NULL,\r
+                  &DataSize,\r
+                  &OsIndication\r
+                  );\r
+  if (EFI_ERROR(Status) ||\r
+      (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) {\r
+    return Status;\r
+  }\r
+\r
+  OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);\r
+  Status = gRT->SetVariable (\r
+                  EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                  sizeof(UINT64),\r
+                  &OsIndication\r
+                  );\r
+  ASSERT(!EFI_ERROR(Status));\r
+\r
+  //\r
+  // Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable\r
+  //\r
+  Status = gRT->SetVariable (\r
+                  EFI_BOOT_NEXT_VARIABLE_NAME,\r
+                  &gEfiGlobalVariableGuid,\r
+                  0,\r
+                  0,\r
+                  NULL\r
+                  );\r
+  ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  This routine is called to clear CapsuleOnDisk Relocation Info variable.\r
+  Total Capsule On Disk length is recorded in this variable\r
+\r
+  @retval EFI_SUCCESS   Capsule On Disk flags are cleared\r
+\r
+**/\r
+EFI_STATUS\r
+CoDClearCapsuleRelocationInfo(\r
+  VOID\r
+  )\r
+{\r
+  return gRT->SetVariable (\r
+                COD_RELOCATION_INFO_VAR_NAME,\r
+                &gEfiCapsuleVendorGuid,\r
+                0,\r
+                0,\r
+                NULL\r
+                );\r
+}\r
+\r
+/**\r
+  Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device\r
+  with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must\r
+  be a full device path.\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  Side Effects:\r
+    Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems\r
+    of the relocation device will be corrupted.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated.\r
+\r
+  @retval EFI_SUCCESS   Capsule on Disk images are sucessfully relocated to the platform-specific device.\r
+\r
+**/\r
+EFI_STATUS\r
+RelocateCapsuleToDisk(\r
+  UINTN     MaxRetry\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  UINTN                           CapsuleOnDiskNum;\r
+  UINTN                           Index;\r
+  UINTN                           DataSize;\r
+  UINT64                          TotalImageSize;\r
+  UINT64                          TotalImageNameSize;\r
+  IMAGE_INFO                      *CapsuleOnDiskBuf;\r
+  EFI_HANDLE                      Handle;\r
+  EFI_HANDLE                      TempHandle;\r
+  EFI_HANDLE                      *HandleBuffer;\r
+  UINTN                           NumberOfHandles;\r
+  EFI_BLOCK_IO_PROTOCOL           *BlockIo;\r
+  UINT8                           *CapsuleDataBuf;\r
+  UINT8                           *CapsulePtr;\r
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+  EFI_FILE_HANDLE                 RootDir;\r
+  EFI_FILE_HANDLE                 TempCodFile;\r
+  UINT64                          TempCodFileSize;\r
+  EFI_DEVICE_PATH                 *TempDevicePath;\r
+  BOOLEAN                         RelocationInfo;\r
+  UINT16                          LoadOptionNumber;\r
+  EFI_CAPSULE_HEADER              FileNameCapsuleHeader;\r
+\r
+  RootDir          = NULL;\r
+  TempCodFile      = NULL;\r
+  HandleBuffer     = NULL;\r
+  CapsuleDataBuf   = NULL;\r
+  CapsuleOnDiskBuf = NULL;\r
+  NumberOfHandles  = 0;\r
+\r
+  DEBUG ((DEBUG_INFO, "CapsuleOnDisk RelocateCapsule Enter\n"));\r
+\r
+  //\r
+  // 1. Load all Capsule On Disks in to memory\r
+  //\r
+  Status = GetAllCapsuleOnDisk(MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, &LoadOptionNumber);\r
+  if (EFI_ERROR(Status) || CapsuleOnDiskNum == 0) {\r
+    DEBUG ((DEBUG_INFO, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // 2. Connect platform special device path as relocation device.\r
+  // If no platform special device path specified or the device path is invalid, use the EFI system partition where\r
+  // stores the capsules as relocation device.\r
+  //\r
+  if (IsDevicePathValid ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), PcdGetSize(PcdCodRelocationDevPath))) {\r
+    Status = EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), &TempHandle);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status));\r
+      goto EXIT;\r
+    }\r
+\r
+    //\r
+    // Connect all the child handle. Partition & FAT drivers are allowed in this case\r
+    //\r
+    gBS->ConnectController (TempHandle, NULL, NULL, TRUE);\r
+    Status = gBS->LocateHandleBuffer(\r
+                    ByProtocol,\r
+                    &gEfiSimpleFileSystemProtocolGuid,\r
+                    NULL,\r
+                    &NumberOfHandles,\r
+                    &HandleBuffer\r
+                    );\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG ((DEBUG_ERROR, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status));\r
+      goto EXIT;\r
+    }\r
+\r
+    //\r
+    // Find first Simple File System Handle which can match PcdCodRelocationDevPath\r
+    //\r
+    for (Index = 0; Index < NumberOfHandles; Index++) {\r
+      Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&TempDevicePath);\r
+      if (EFI_ERROR(Status)) {\r
+        continue;\r
+      }\r
+\r
+      DataSize = GetDevicePathSize((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath)) - sizeof(EFI_DEVICE_PATH);\r
+      if (0 == CompareMem((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), TempDevicePath, DataSize)) {\r
+        Handle = HandleBuffer[Index];\r
+        break;\r
+      }\r
+    }\r
+\r
+    FreePool(HandleBuffer);\r
+\r
+    if (Index == NumberOfHandles) {\r
+      DEBUG ((DEBUG_ERROR, "RelocateCapsule: No simple file system protocol found.\n"));\r
+      Status = EFI_NOT_FOUND;\r
+    }\r
+  }\r
+\r
+  Status = gBS->HandleProtocol(Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);\r
+  if (EFI_ERROR(Status) || BlockIo->Media->ReadOnly) {\r
+    DEBUG((DEBUG_ERROR, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));\r
+    goto EXIT;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+  if (EFI_ERROR(Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Check if device used to relocate Capsule On Disk is big enough\r
+  //\r
+  TotalImageSize     = 0;\r
+  TotalImageNameSize = 0;\r
+  for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
+    //\r
+    // Overflow check\r
+    //\r
+    if (MAX_ADDRESS - (UINTN)TotalImageSize <= CapsuleOnDiskBuf[Index].FileInfo->FileSize) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto EXIT;\r
+    }\r
+\r
+    if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName)) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto EXIT;\r
+    }\r
+\r
+    TotalImageSize     += CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
+    TotalImageNameSize += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+    DEBUG((DEBUG_INFO, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf[Index].FileInfo->FileName, CapsuleOnDiskBuf[Index].FileInfo->FileSize));\r
+  }\r
+\r
+  DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize));\r
+  DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize));\r
+\r
+  if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= sizeof(UINT64) * 2 ||\r
+      MAX_ADDRESS - (UINTN)TotalImageSize <= (UINTN)TotalImageNameSize + sizeof(UINT64) * 2) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto EXIT;\r
+  }\r
+\r
+  TempCodFileSize = sizeof(UINT64) + TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;\r
+\r
+  //\r
+  // Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly\r
+  //\r
+  if (DivU64x32(TempCodFileSize, BlockIo->Media->BlockSize) > BlockIo->Media->LastBlock) {\r
+    DEBUG((DEBUG_ERROR, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));\r
+    DEBUG((DEBUG_ERROR, "TotalImageSize = %x\n", TotalImageSize));\r
+    DEBUG((DEBUG_ERROR, "TotalImageNameSize = %x\n", TotalImageNameSize));\r
+    DEBUG((DEBUG_ERROR, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo->Media->BlockSize, BlockIo->Media->LastBlock));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto EXIT;\r
+  }\r
+\r
+  CapsuleDataBuf = AllocatePool((UINTN) TempCodFileSize);\r
+  if (CapsuleDataBuf == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // First UINT64 reserved for total image size, including capsule name capsule.\r
+  //\r
+  *(UINT64 *) CapsuleDataBuf = TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;\r
+\r
+  //\r
+  // Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write\r
+  //\r
+  for (Index = 0, CapsulePtr = CapsuleDataBuf + sizeof(UINT64); Index < CapsuleOnDiskNum; Index++) {\r
+    CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].ImageAddress, (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize);\r
+    CapsulePtr += CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
+  }\r
+\r
+  //\r
+  // Line the capsule header for capsule name capsule.\r
+  //\r
+  CopyGuid(&FileNameCapsuleHeader.CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid);\r
+  FileNameCapsuleHeader.CapsuleImageSize = (UINT32) TotalImageNameSize + sizeof(EFI_CAPSULE_HEADER);\r
+  FileNameCapsuleHeader.Flags            = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r
+  FileNameCapsuleHeader.HeaderSize       = sizeof(EFI_CAPSULE_HEADER);\r
+  CopyMem(CapsulePtr, &FileNameCapsuleHeader, FileNameCapsuleHeader.HeaderSize);\r
+  CapsulePtr += FileNameCapsuleHeader.HeaderSize;\r
+\r
+  //\r
+  // Line up all the Capsule file names.\r
+  //\r
+  for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
+    CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].FileInfo->FileName, StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName));\r
+    CapsulePtr += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+  }\r
+\r
+  //\r
+  // 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir\r
+  //\r
+  Status = Fs->OpenVolume(Fs, &RootDir);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG((DEBUG_ERROR, "RelocateCapsule: OpenVolume error. %x\n", Status));\r
+    goto EXIT;\r
+  }\r
+\r
+  Status = RootDir->Open(\r
+                      RootDir,\r
+                      &TempCodFile,\r
+                      (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
+                      EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
+                      0\r
+                      );\r
+  if (!EFI_ERROR(Status)) {\r
+    //\r
+    // Error handling code to prevent malicious code to hold this file to block capsule on disk\r
+    //\r
+    TempCodFile->Delete(TempCodFile);\r
+  }\r
+  Status = RootDir->Open(\r
+                      RootDir,\r
+                      &TempCodFile,\r
+                      (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
+                      EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,\r
+                      0\r
+                      );\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG((DEBUG_ERROR, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status));\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Always write at the begining of TempCap file\r
+  //\r
+  DataSize = (UINTN) TempCodFileSize;\r
+  Status = TempCodFile->Write(\r
+                          TempCodFile,\r
+                          &DataSize,\r
+                          CapsuleDataBuf\r
+                          );\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG((DEBUG_ERROR, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status));\r
+    goto EXIT;\r
+  }\r
+\r
+  if (DataSize != TempCodFileSize) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Save Capsule On Disk relocation info to "CodRelocationInfo" Var\r
+  // It is used in next reboot by TCB\r
+  //\r
+  RelocationInfo = TRUE;\r
+  Status = gRT->SetVariable(\r
+                   COD_RELOCATION_INFO_VAR_NAME,\r
+                   &gEfiCapsuleVendorGuid,\r
+                   EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                   sizeof (BOOLEAN),\r
+                   &RelocationInfo\r
+                   );\r
+  //\r
+  // Save the LoadOptionNumber of the boot option, where the capsule is relocated,\r
+  // into "CodRelocationLoadOption" var. It is used in next reboot after capsule is\r
+  // updated out of TCB to remove the TempCoDFile.\r
+  //\r
+  Status = gRT->SetVariable(\r
+                   COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
+                   &gEfiCapsuleVendorGuid,\r
+                   EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                   sizeof (UINT16),\r
+                   &LoadOptionNumber\r
+                   );\r
+\r
+EXIT:\r
+\r
+  if (CapsuleDataBuf != NULL) {\r
+    FreePool(CapsuleDataBuf);\r
+  }\r
+\r
+  if (CapsuleOnDiskBuf != NULL) {\r
+    //\r
+    // Free resources allocated by CodLibGetAllCapsuleOnDisk\r
+    //\r
+    for (Index = 0; Index < CapsuleOnDiskNum; Index++ ) {\r
+      FreePool(CapsuleOnDiskBuf[Index].ImageAddress);\r
+      FreePool(CapsuleOnDiskBuf[Index].FileInfo);\r
+    }\r
+    FreePool(CapsuleOnDiskBuf);\r
+  }\r
+\r
+  if (TempCodFile != NULL) {\r
+    if (EFI_ERROR(Status)) {\r
+      TempCodFile->Delete (TempCodFile);\r
+    } else {\r
+      TempCodFile->Close (TempCodFile);\r
+    }\r
+  }\r
+\r
+  if (RootDir != NULL) {\r
+    RootDir->Close (RootDir);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.\r
+  Relocate Capsule On Disk to memory and call UpdateCapsule().\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated.\r
+\r
+  @retval EFI_SUCCESS   Deliver capsule through Capsule In Ram successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+RelocateCapsuleToRam (\r
+  UINTN    MaxRetry\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINTN                         CapsuleOnDiskNum;\r
+  IMAGE_INFO                    *CapsuleOnDiskBuf;\r
+  EFI_HANDLE                    Handle;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR  *BlockDescriptors;\r
+  VOID                          **CapsuleBuffer;\r
+  UINTN                         *CapsuleSize;\r
+  EFI_CAPSULE_HEADER            *FileNameCapsule;\r
+  UINTN                         Index;\r
+  UINT8                         *StringBuf;\r
+  UINTN                         StringSize;\r
+  UINTN                         TotalStringSize;\r
+\r
+  CapsuleOnDiskBuf = NULL;\r
+  BlockDescriptors = NULL;\r
+  CapsuleBuffer    = NULL;\r
+  CapsuleSize      = NULL;\r
+  FileNameCapsule  = NULL;\r
+  TotalStringSize  = 0;\r
+\r
+  //\r
+  // 1. Load all Capsule On Disks into memory\r
+  //\r
+  Status = GetAllCapsuleOnDisk (MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, NULL);\r
+  if (EFI_ERROR (Status) || CapsuleOnDiskNum == 0) {\r
+    DEBUG ((DEBUG_ERROR, "GetAllCapsuleOnDisk Status - 0x%x\n", Status));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // 2. Add a capsule for Capsule file name strings\r
+  //\r
+  CapsuleBuffer = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (VOID *));\r
+  if (CapsuleBuffer == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  CapsuleSize = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (UINTN));\r
+  if (CapsuleSize == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));\r
+    FreePool (CapsuleBuffer);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
+    CapsuleBuffer[Index] = (VOID *)(UINTN) CapsuleOnDiskBuf[Index].ImageAddress;\r
+    CapsuleSize[Index] = (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
+    TotalStringSize += StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+  }\r
+\r
+  FileNameCapsule = AllocateZeroPool (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);\r
+  if (FileNameCapsule == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "Fail to allocate memory for name capsule.\n"));\r
+    FreePool (CapsuleBuffer);\r
+    FreePool (CapsuleSize);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  FileNameCapsule->CapsuleImageSize = (UINT32) (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);\r
+  FileNameCapsule->Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r
+  FileNameCapsule->HeaderSize = sizeof (EFI_CAPSULE_HEADER);\r
+  CopyGuid (&(FileNameCapsule->CapsuleGuid), &gEdkiiCapsuleOnDiskNameGuid);\r
+\r
+  StringBuf = (UINT8 *)FileNameCapsule + FileNameCapsule->HeaderSize;\r
+  for (Index = 0; Index < CapsuleOnDiskNum; Index ++) {\r
+    StringSize = StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+    CopyMem (StringBuf, CapsuleOnDiskBuf[Index].FileInfo->FileName, StringSize);\r
+    StringBuf += StringSize;\r
+  }\r
+\r
+  CapsuleBuffer[CapsuleOnDiskNum] = FileNameCapsule;\r
+  CapsuleSize[CapsuleOnDiskNum] = TotalStringSize + sizeof (EFI_CAPSULE_HEADER);\r
+\r
+  //\r
+  // 3. Build Gather list for the capsules\r
+  //\r
+  Status = BuildGatherList (CapsuleBuffer, CapsuleSize, CapsuleOnDiskNum + 1, &BlockDescriptors);\r
+  if (EFI_ERROR (Status) || BlockDescriptors == NULL) {\r
+    FreePool (CapsuleBuffer);\r
+    FreePool (CapsuleSize);\r
+    FreePool (FileNameCapsule);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // 4. Call UpdateCapsule() service\r
+  //\r
+  Status = gRT->UpdateCapsule((EFI_CAPSULE_HEADER **) CapsuleBuffer, CapsuleOnDiskNum + 1, (UINTN) BlockDescriptors);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Relocate Capsule on Disk from EFI system partition.\r
+\r
+  Two solution to deliver Capsule On Disk:\r
+  Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().\r
+  Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage\r
+  device with BlockIo protocol.\r
+\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  Side Effects:\r
+    Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.\r
+    Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file\r
+  systems of the relocation device will be corrupted.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+  @retval EFI_SUCCESS   Capsule on Disk images are successfully relocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRelocateCapsule(\r
+  UINTN     MaxRetry\r
+  )\r
+{\r
+  if (!PcdGetBool (PcdCapsuleOnDiskSupport)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Clear CapsuleOnDisk Flag firstly.\r
+  //\r
+  CoDClearCapsuleOnDiskFlag ();\r
+\r
+  //\r
+  // If Capsule In Ram is supported, delivery capsules through memory\r
+  //\r
+  if (PcdGetBool (PcdCapsuleInRamSupport)) {\r
+    DEBUG ((DEBUG_INFO, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));\r
+    return RelocateCapsuleToRam (MaxRetry);\r
+  } else {\r
+    DEBUG ((DEBUG_INFO, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName)));\r
+    return RelocateCapsuleToDisk (MaxRetry);\r
+  }\r
+}\r
+\r
+/**\r
+  Remove the temp file from the root of EFI System Partition.\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+  @retval EFI_SUCCESS   Remove the temp file successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRemoveTempFile (\r
+  UINTN    MaxRetry\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINTN                            DataSize;\r
+  UINT16                           *LoadOptionNumber;\r
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *Fs;\r
+  EFI_HANDLE                       FsHandle;\r
+  EFI_FILE_HANDLE                  RootDir;\r
+  EFI_FILE_HANDLE                  TempCodFile;\r
+\r
+  RootDir     = NULL;\r
+  TempCodFile = NULL;\r
+  DataSize    = sizeof(UINT16);\r
+\r
+  LoadOptionNumber = AllocatePool (sizeof(UINT16));\r
+  if (LoadOptionNumber == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Check if capsule files are relocated\r
+  //\r
+  Status = gRT->GetVariable (\r
+                  COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
+                  &gEfiCapsuleVendorGuid,\r
+                  NULL,\r
+                  &DataSize,\r
+                  (VOID *)LoadOptionNumber\r
+                  );\r
+  if (EFI_ERROR(Status) || DataSize != sizeof(UINT16)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Get the EFI file system from the boot option where the capsules are relocated\r
+  //\r
+  Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &LoadOptionNumber, &FsHandle);\r
+  if (EFI_ERROR(Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol(FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+  if (EFI_ERROR(Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  Status = Fs->OpenVolume(Fs, &RootDir);\r
+  if (EFI_ERROR(Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  //\r
+  // Delete the TempCoDFile\r
+  //\r
+  Status = RootDir->Open(\r
+                      RootDir,\r
+                      &TempCodFile,\r
+                      (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
+                      EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
+                      0\r
+                      );\r
+  if (EFI_ERROR(Status)) {\r
+    goto EXIT;\r
+  }\r
+\r
+  TempCodFile->Delete(TempCodFile);\r
+\r
+  //\r
+  // Clear "CoDRelocationLoadOption" variable\r
+  //\r
+  Status = gRT->SetVariable (\r
+             COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
+             &gEfiCapsuleVendorGuid,\r
+             0,\r
+             0,\r
+             NULL\r
+             );\r
+\r
+EXIT:\r
+  if (LoadOptionNumber != NULL) {\r
+    FreePool (LoadOptionNumber);\r
+  }\r
+\r
+  if (RootDir != NULL) {\r
+    RootDir->Close(RootDir);\r
+  }\r
+\r
+  return Status;\r
+}\r
diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h b/MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.h
new file mode 100644 (file)
index 0000000..4300e32
--- /dev/null
@@ -0,0 +1,75 @@
+/** @file\r
+  Defines several datastructures used by Capsule On Disk feature.\r
+  They are mainly used for FAT files.\r
+\r
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#ifndef _CAPSULES_ON_DISK_H_\r
+#define _CAPSULES_ON_DISK_H_\r
+\r
+#include <Uefi.h>\r
+#include <Pi/PiMultiPhase.h>\r
+\r
+#include <Library/UefiLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiRuntimeLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/FileHandleLib.h>\r
+#include <Library/CapsuleLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/UefiBootManagerLib.h>\r
+\r
+#include <Protocol/SimpleFileSystem.h>\r
+#include <Protocol/DiskIo.h>\r
+#include <Protocol/BlockIo.h>\r
+\r
+#include <Guid/CapsuleVendor.h>\r
+#include <Guid/FileInfo.h>\r
+#include <Guid/GlobalVariable.h>\r
+\r
+//\r
+// This data structure is the part of FILE_INFO_ENTRY\r
+//\r
+#define FILE_INFO_SIGNATURE SIGNATURE_32 ('F', 'L', 'I', 'F')\r
+\r
+//\r
+// LoadOptionNumber of the boot option where the capsules is relocated.\r
+//\r
+#define COD_RELOCATION_LOAD_OPTION_VAR_NAME   L"CodRelocationLoadOption"\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
+#define MAX_FILE_INFO_LEN    (OFFSET_OF(EFI_FILE_INFO, FileName) + MAX_FILE_NAME_LEN)\r
+\r
+typedef struct {\r
+  UINTN           Signature;\r
+  LIST_ENTRY      Link;                  ///  Linked list members.\r
+  EFI_FILE_INFO   *FileInfo;             ///  Pointer to the FileInfo struct for this file or NULL.\r
+  CHAR16          *FileNameFirstPart;    ///  Text to the left of right-most period in the file name. String is capitialized\r
+  CHAR16          *FileNameSecondPart;   ///  Text to the right of right-most period in the file name.String is capitialized. Maybe NULL\r
+} FILE_INFO_ENTRY;\r
+\r
+typedef struct {\r
+  //\r
+  // image address.\r
+  //\r
+  VOID             *ImageAddress;\r
+  //\r
+  // The file info of the image comes from.\r
+  //  if FileInfo == NULL. means image does not come from file\r
+  //\r
+  EFI_FILE_INFO    *FileInfo;\r
+} IMAGE_INFO;\r
+\r
+#endif // _CAPSULES_ON_DISK_H_\r
index f38ab69e38fb22543d37c62aa0a331c3637b7609..95aa9de0878b8f7abcd72f74fced4e2ebbc3d0ff 100644 (file)
@@ -10,7 +10,7 @@
   ValidateFmpCapsule(), and DisplayCapsuleImage() receives untrusted input and\r
   performs basic validation.\r
 \r
-  Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
@@ -80,6 +80,7 @@ RecordCapsuleStatusVariable (
   @param[in] PayloadIndex   FMP payload index\r
   @param[in] ImageHeader    FMP image header\r
   @param[in] FmpDevicePath  DevicePath associated with the FMP producer\r
+  @param[in] CapFileName    Capsule file name\r
 \r
   @retval EFI_SUCCESS          The capsule status variable is recorded.\r
   @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.\r
@@ -90,7 +91,8 @@ RecordFmpCapsuleStatusVariable (
   IN EFI_STATUS                                    CapsuleStatus,\r
   IN UINTN                                         PayloadIndex,\r
   IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *ImageHeader,\r
-  IN EFI_DEVICE_PATH_PROTOCOL                      *FmpDevicePath OPTIONAL\r
+  IN EFI_DEVICE_PATH_PROTOCOL                      *FmpDevicePath, OPTIONAL\r
+  IN CHAR16                                        *CapFileName    OPTIONAL\r
   );\r
 \r
 /**\r
@@ -109,6 +111,22 @@ UpdateImageProgress (
   IN UINTN  Completion\r
   );\r
 \r
+/**\r
+  Return if this capsule is a capsule name capsule, based upon CapsuleHeader.\r
+\r
+  @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER\r
+\r
+  @retval TRUE  It is a capsule name capsule.\r
+  @retval FALSE It is not a capsule name capsule.\r
+**/\r
+BOOLEAN\r
+IsCapsuleNameCapsule (\r
+  IN EFI_CAPSULE_HEADER         *CapsuleHeader\r
+  )\r
+{\r
+  return CompareGuid (&CapsuleHeader->CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid);\r
+}\r
+\r
 /**\r
   Return if this CapsuleGuid is a FMP capsule GUID or not.\r
 \r
@@ -1034,11 +1052,12 @@ StartFmpImage (
 /**\r
   Record FMP capsule status.\r
 \r
-  @param[in]  Handle        A FMP handle.\r
+  @param[in] Handle         A FMP handle.\r
   @param[in] CapsuleHeader  The capsule image header\r
   @param[in] CapsuleStatus  The capsule process stauts\r
   @param[in] PayloadIndex   FMP payload index\r
   @param[in] ImageHeader    FMP image header\r
+  @param[in] CapFileName    Capsule file name\r
 **/\r
 VOID\r
 RecordFmpCapsuleStatus (\r
@@ -1046,7 +1065,8 @@ RecordFmpCapsuleStatus (
   IN EFI_CAPSULE_HEADER                            *CapsuleHeader,\r
   IN EFI_STATUS                                    CapsuleStatus,\r
   IN UINTN                                         PayloadIndex,\r
-  IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *ImageHeader\r
+  IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *ImageHeader,\r
+  IN CHAR16                                        *CapFileName   OPTIONAL\r
   )\r
 {\r
   EFI_STATUS                                    Status;\r
@@ -1070,7 +1090,8 @@ RecordFmpCapsuleStatus (
     CapsuleStatus,\r
     PayloadIndex,\r
     ImageHeader,\r
-    FmpDevicePath\r
+    FmpDevicePath,\r
+    CapFileName\r
     );\r
 \r
   //\r
@@ -1115,6 +1136,7 @@ RecordFmpCapsuleStatus (
   This function need support nested FMP capsule.\r
 \r
   @param[in]  CapsuleHeader         Points to a capsule header.\r
+  @param[in]  CapFileName           Capsule file name.\r
   @param[out] ResetRequired         Indicates whether reset is required or not.\r
 \r
   @retval EFI_SUCESS            Process Capsule Image successfully.\r
@@ -1126,6 +1148,7 @@ RecordFmpCapsuleStatus (
 EFI_STATUS\r
 ProcessFmpCapsuleImage (\r
   IN EFI_CAPSULE_HEADER  *CapsuleHeader,\r
+  IN CHAR16              *CapFileName,  OPTIONAL\r
   OUT BOOLEAN            *ResetRequired OPTIONAL\r
   )\r
 {\r
@@ -1145,7 +1168,7 @@ ProcessFmpCapsuleImage (
   BOOLEAN                                       Abort;\r
 \r
   if (!IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {\r
-    return ProcessFmpCapsuleImage ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), ResetRequired);\r
+    return ProcessFmpCapsuleImage ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), CapFileName, ResetRequired);\r
   }\r
 \r
   NotReady = FALSE;\r
@@ -1227,7 +1250,8 @@ ProcessFmpCapsuleImage (
         CapsuleHeader,\r
         EFI_NOT_READY,\r
         Index - FmpCapsuleHeader->EmbeddedDriverCount,\r
-        ImageHeader\r
+        ImageHeader,\r
+        CapFileName\r
         );\r
       continue;\r
     }\r
@@ -1239,7 +1263,8 @@ ProcessFmpCapsuleImage (
           CapsuleHeader,\r
           EFI_ABORTED,\r
           Index - FmpCapsuleHeader->EmbeddedDriverCount,\r
-          ImageHeader\r
+          ImageHeader,\r
+          CapFileName\r
           );\r
         continue;\r
       }\r
@@ -1262,7 +1287,8 @@ ProcessFmpCapsuleImage (
         CapsuleHeader,\r
         Status,\r
         Index - FmpCapsuleHeader->EmbeddedDriverCount,\r
-        ImageHeader\r
+        ImageHeader,\r
+        CapFileName\r
         );\r
     }\r
     if (HandleBuffer != NULL) {\r
@@ -1414,6 +1440,13 @@ SupportCapsuleImage (
     return EFI_SUCCESS;\r
   }\r
 \r
+  //\r
+  // Check capsule file name capsule\r
+  //\r
+  if (IsCapsuleNameCapsule(CapsuleHeader)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
   if (IsFmpCapsule(CapsuleHeader)) {\r
     //\r
     // Fake capsule header is valid case in QueryCapsuleCpapbilities().\r
@@ -1436,6 +1469,7 @@ SupportCapsuleImage (
   Caution: This function may receive untrusted input.\r
 \r
   @param[in]  CapsuleHeader         Points to a capsule header.\r
+  @param[in]  CapFileName           Capsule file name.\r
   @param[out] ResetRequired         Indicates whether reset is required or not.\r
 \r
   @retval EFI_SUCESS            Process Capsule Image successfully.\r
@@ -1447,6 +1481,7 @@ EFI_STATUS
 EFIAPI\r
 ProcessThisCapsuleImage (\r
   IN EFI_CAPSULE_HEADER  *CapsuleHeader,\r
+  IN CHAR16              *CapFileName,  OPTIONAL\r
   OUT BOOLEAN            *ResetRequired OPTIONAL\r
   )\r
 {\r
@@ -1484,7 +1519,7 @@ ProcessThisCapsuleImage (
     // Process EFI FMP Capsule\r
     //\r
     DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n"));\r
-    Status = ProcessFmpCapsuleImage(CapsuleHeader, ResetRequired);\r
+    Status = ProcessFmpCapsuleImage(CapsuleHeader, CapFileName, ResetRequired);\r
     DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status));\r
 \r
     return Status;\r
@@ -1511,7 +1546,7 @@ ProcessCapsuleImage (
   IN EFI_CAPSULE_HEADER  *CapsuleHeader\r
   )\r
 {\r
-  return ProcessThisCapsuleImage (CapsuleHeader, NULL);\r
+  return ProcessThisCapsuleImage (CapsuleHeader, NULL, NULL);\r
 }\r
 \r
 /**\r
index 14c3d19bc393cf54241fbde0620c5b653ad7de5d..05de4299fba92f5ccc8ac7db41722af86d6bbf6b 100644 (file)
@@ -3,7 +3,7 @@
 #\r
 #  Capsule library instance for DXE_DRIVER module types.\r
 #\r
-#  Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
+#  Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
 #  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 #\r
 ##\r
@@ -29,6 +29,8 @@
   DxeCapsuleLib.c\r
   DxeCapsuleProcessLib.c\r
   DxeCapsuleReportLib.c\r
+  CapsuleOnDisk.c\r
+  CapsuleOnDisk.h\r
 \r
 [Packages]\r
   MdePkg/MdePkg.dec\r
@@ -47,6 +49,8 @@
   HobLib\r
   BmpSupportLib\r
   DisplayUpdateProgressLib\r
+  FileHandleLib\r
+  UefiBootManagerLib\r
 \r
 [Pcd]\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleMax                               ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareSuccess   ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareFailed    ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeResettingSystem         ## CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleInRamSupport                      ## CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleOnDiskSupport                     ## CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCodRelocationDevPath                     ## SOMETIMES_CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCoDRelocationFileName                    ## CONSUMES\r
 \r
 [Protocols]\r
   gEsrtManagementProtocolGuid                   ## CONSUMES\r
   gEfiFirmwareManagementProtocolGuid            ## CONSUMES\r
   gEdkiiVariableLockProtocolGuid                ## SOMETIMES_CONSUMES\r
   gEdkiiFirmwareManagementProgressProtocolGuid  ## SOMETIMES_CONSUMES\r
+  gEfiSimpleFileSystemProtocolGuid              ## SOMETIMES_CONSUMES\r
+  gEfiBlockIoProtocolGuid                       ## CONSUMES\r
+  gEfiDiskIoProtocolGuid                        ## CONSUMES\r
 \r
 [Guids]\r
   gEfiFmpCapsuleGuid                      ## SOMETIMES_CONSUMES ## GUID\r
   gEfiCapsuleReportGuid\r
   gEfiCapsuleVendorGuid                   ## SOMETIMES_CONSUMES ## Variable:L"CapsuleUpdateData"\r
   gEfiEndOfDxeEventGroupGuid              ## CONSUMES ## Event\r
+  gEfiPartTypeSystemPartGuid              ## SOMETIMES_CONSUMES\r
+  gEfiCapsuleVendorGuid                   ## SOMETIMES_CONSUMES ## Variable:L"CodRelocationInfo"\r
+  ## SOMETIMES_CONSUMES ## Variable:L"OsIndications"\r
+  ## SOMETIMES_PRODUCES ## Variable:L"OsIndications"\r
+  ## SOMETIMES_CONSUMES ## Variable:L"BootNext"\r
+  ## SOMETIMES_PRODUCES ## Variable:L"BootNext"\r
+  gEfiGlobalVariableGuid\r
+  gEdkiiCapsuleOnDiskNameGuid             ## SOMETIMES_CONSUMES ## GUID\r
 \r
 [Depex]\r
   gEfiVariableWriteArchProtocolGuid\r
index 5e2d2b87a8d0831f1520ccb6c4886c488438c125..9c9cd9e373c964285e8a4cbe9b1b172f51d55c4d 100644 (file)
@@ -9,7 +9,7 @@
   ProcessCapsules(), ProcessTheseCapsules() will receive untrusted\r
   input and do basic validation.\r
 \r
-  Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
@@ -92,10 +92,41 @@ IsValidCapsuleHeader (
   IN UINT64              CapsuleSize\r
   );\r
 \r
+/**\r
+  Return if this capsule is a capsule name capsule, based upon CapsuleHeader.\r
+\r
+  @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER\r
+\r
+  @retval TRUE  It is a capsule name capsule.\r
+  @retval FALSE It is not a capsule name capsule.\r
+**/\r
+BOOLEAN\r
+IsCapsuleNameCapsule (\r
+  IN EFI_CAPSULE_HEADER         *CapsuleHeader\r
+  );\r
+\r
+/**\r
+  Check the integrity of the capsule name capsule.\r
+  If the capsule is vaild, return the physical address of each capsule name string.\r
+\r
+  @param[in]  CapsuleHeader   Pointer to the capsule header of a capsule name capsule.\r
+  @param[out] CapsuleNameNum  Number of capsule name.\r
+\r
+  @retval NULL                Capsule name capsule is not valid.\r
+  @retval CapsuleNameBuf      Array of capsule name physical address.\r
+\r
+**/\r
+EFI_PHYSICAL_ADDRESS *\r
+ValidateCapsuleNameCapsuleIntegrity (\r
+  IN  EFI_CAPSULE_HEADER            *CapsuleHeader,\r
+  OUT UINTN                         *CapsuleNameNum\r
+  );\r
+\r
 extern BOOLEAN                   mDxeCapsuleLibEndOfDxe;\r
 BOOLEAN                          mNeedReset = FALSE;\r
 \r
 VOID                        **mCapsulePtr;\r
+CHAR16                      **mCapsuleNamePtr;\r
 EFI_STATUS                  *mCapsuleStatusArray;\r
 UINT32                      mCapsuleTotalNumber;\r
 \r
@@ -116,6 +147,7 @@ EFI_STATUS
 EFIAPI\r
 ProcessThisCapsuleImage (\r
   IN EFI_CAPSULE_HEADER  *CapsuleHeader,\r
+  IN CHAR16              *CapFileName,  OPTIONAL\r
   OUT BOOLEAN            *ResetRequired OPTIONAL\r
   );\r
 \r
@@ -185,6 +217,18 @@ InitCapsulePtr (
 {\r
   EFI_PEI_HOB_POINTERS        HobPointer;\r
   UINTN                       Index;\r
+  UINTN                       Index2;\r
+  UINTN                       Index3;\r
+  UINTN                       CapsuleNameNumber;\r
+  UINTN                       CapsuleNameTotalNumber;\r
+  UINTN                       CapsuleNameCapsuleTotalNumber;\r
+  VOID                        **CapsuleNameCapsulePtr;\r
+  EFI_PHYSICAL_ADDRESS        *CapsuleNameAddress;\r
+\r
+  CapsuleNameNumber             = 0;\r
+  CapsuleNameTotalNumber        = 0;\r
+  CapsuleNameCapsuleTotalNumber = 0;\r
+  CapsuleNameCapsulePtr         = NULL;\r
 \r
   //\r
   // Find all capsule images from hob\r
@@ -194,7 +238,11 @@ InitCapsulePtr (
     if (!IsValidCapsuleHeader((VOID *)(UINTN)HobPointer.Capsule->BaseAddress, HobPointer.Capsule->Length)) {\r
       HobPointer.Header->HobType = EFI_HOB_TYPE_UNUSED; // Mark this hob as invalid\r
     } else {\r
-      mCapsuleTotalNumber++;\r
+      if (IsCapsuleNameCapsule((VOID *)(UINTN)HobPointer.Capsule->BaseAddress)) {\r
+        CapsuleNameCapsuleTotalNumber++;\r
+      } else {\r
+        mCapsuleTotalNumber++;\r
+      }\r
     }\r
     HobPointer.Raw = GET_NEXT_HOB (HobPointer);\r
   }\r
@@ -224,15 +272,72 @@ InitCapsulePtr (
   }\r
   SetMemN (mCapsuleStatusArray, sizeof (EFI_STATUS) * mCapsuleTotalNumber, EFI_NOT_READY);\r
 \r
+  if (CapsuleNameCapsuleTotalNumber != 0) {\r
+    CapsuleNameCapsulePtr =  (VOID **) AllocateZeroPool (sizeof (VOID *) * CapsuleNameCapsuleTotalNumber);\r
+    if (CapsuleNameCapsulePtr == NULL) {\r
+      DEBUG ((DEBUG_ERROR, "Allocate CapsuleNameCapsulePtr fail!\n"));\r
+      FreePool (mCapsulePtr);\r
+      FreePool (mCapsuleStatusArray);\r
+      mCapsulePtr         = NULL;\r
+      mCapsuleStatusArray = NULL;\r
+      mCapsuleTotalNumber = 0;\r
+      return ;\r
+    }\r
+  }\r
+\r
   //\r
   // Find all capsule images from hob\r
   //\r
   HobPointer.Raw = GetHobList ();\r
-  Index = 0;\r
+  Index  = 0;\r
+  Index2 = 0;\r
   while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) {\r
-    mCapsulePtr [Index++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress;\r
+    if (!IsCapsuleNameCapsule ((VOID *) (UINTN) HobPointer.Capsule->BaseAddress)) {\r
+      mCapsulePtr [Index++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress;\r
+    } else {\r
+      CapsuleNameCapsulePtr [Index2++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress;\r
+    }\r
     HobPointer.Raw = GET_NEXT_HOB (HobPointer);\r
   }\r
+\r
+  //\r
+  // Find Capsule On Disk Names\r
+  //\r
+  for (Index = 0; Index < CapsuleNameCapsuleTotalNumber; Index ++) {\r
+    CapsuleNameAddress = ValidateCapsuleNameCapsuleIntegrity (CapsuleNameCapsulePtr[Index], &CapsuleNameNumber);\r
+    if (CapsuleNameAddress != NULL ) {\r
+      CapsuleNameTotalNumber += CapsuleNameNumber;\r
+    }\r
+  }\r
+\r
+  if (CapsuleNameTotalNumber == mCapsuleTotalNumber) {\r
+    mCapsuleNamePtr = (CHAR16 **) AllocateZeroPool (sizeof (CHAR16 *) * mCapsuleTotalNumber);\r
+    if (mCapsuleNamePtr == NULL) {\r
+      DEBUG ((DEBUG_ERROR, "Allocate mCapsuleNamePtr fail!\n"));\r
+      FreePool (mCapsulePtr);\r
+      FreePool (mCapsuleStatusArray);\r
+      FreePool (CapsuleNameCapsulePtr);\r
+      mCapsulePtr         = NULL;\r
+      mCapsuleStatusArray = NULL;\r
+      mCapsuleTotalNumber = 0;\r
+      return ;\r
+    }\r
+\r
+    for (Index = 0, Index3 = 0; Index < CapsuleNameCapsuleTotalNumber; Index ++) {\r
+      CapsuleNameAddress = ValidateCapsuleNameCapsuleIntegrity (CapsuleNameCapsulePtr[Index], &CapsuleNameNumber);\r
+      if (CapsuleNameAddress != NULL ) {\r
+        for (Index2 = 0; Index2 < CapsuleNameNumber; Index2 ++) {\r
+          mCapsuleNamePtr[Index3 ++] = (CHAR16 *)(UINTN) CapsuleNameAddress[Index2];\r
+        }\r
+      }\r
+    }\r
+  } else {\r
+    mCapsuleNamePtr = NULL;\r
+  }\r
+\r
+  if (CapsuleNameCapsulePtr != NULL) {\r
+    FreePool (CapsuleNameCapsulePtr);\r
+  }\r
 }\r
 \r
 /**\r
@@ -396,6 +501,7 @@ ProcessTheseCapsules (
   ESRT_MANAGEMENT_PROTOCOL    *EsrtManagement;\r
   UINT16                      EmbeddedDriverCount;\r
   BOOLEAN                     ResetRequired;\r
+  CHAR16                      *CapsuleName;\r
 \r
   REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesBegin)));\r
 \r
@@ -408,6 +514,7 @@ ProcessTheseCapsules (
     // We didn't find a hob, so had no errors.\r
     //\r
     DEBUG ((DEBUG_ERROR, "We can not find capsule data in capsule update boot mode.\n"));\r
+    mNeedReset = TRUE;\r
     return EFI_SUCCESS;\r
   }\r
 \r
@@ -430,10 +537,11 @@ ProcessTheseCapsules (
   //\r
   for (Index = 0; Index < mCapsuleTotalNumber; Index++) {\r
     CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];\r
+    CapsuleName = (mCapsuleNamePtr == NULL) ? NULL : mCapsuleNamePtr[Index];\r
     if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {\r
       DEBUG ((DEBUG_INFO, "ProcessThisCapsuleImage (Ux) - 0x%x\n", CapsuleHeader));\r
       DEBUG ((DEBUG_INFO, "Display logo capsule is found.\n"));\r
-      Status = ProcessThisCapsuleImage (CapsuleHeader, NULL);\r
+      Status = ProcessThisCapsuleImage (CapsuleHeader, CapsuleName, NULL);\r
       mCapsuleStatusArray [Index] = EFI_SUCCESS;\r
       DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage (Ux) - %r\n", Status));\r
       break;\r
@@ -451,6 +559,7 @@ ProcessTheseCapsules (
       continue;\r
     }\r
     CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];\r
+    CapsuleName = (mCapsuleNamePtr == NULL) ? NULL : mCapsuleNamePtr[Index];\r
     if (!CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {\r
       //\r
       // Call capsule library to process capsule image.\r
@@ -471,7 +580,7 @@ ProcessTheseCapsules (
       if ((!FirstRound) || (EmbeddedDriverCount == 0)) {\r
         DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage - 0x%x\n", CapsuleHeader));\r
         ResetRequired = FALSE;\r
-        Status = ProcessThisCapsuleImage (CapsuleHeader, &ResetRequired);\r
+        Status = ProcessThisCapsuleImage (CapsuleHeader, CapsuleName, &ResetRequired);\r
         mCapsuleStatusArray [Index] = Status;\r
         DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage - %r\n", Status));\r
 \r
@@ -530,7 +639,8 @@ DoResetSystem (
   Caution: This function may receive untrusted input.\r
 \r
   The capsules reported in EFI_HOB_UEFI_CAPSULE are processed.\r
-  If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing.\r
+  If there is no EFI_HOB_UEFI_CAPSULE, it means error occurs, force reset to\r
+  normal boot path.\r
 \r
   This routine should be called twice in BDS.\r
   1) The first call must be before EndOfDxe. The system capsules is processed.\r
index 6ad766d65a48d6f6374595d106b4fef4193e1ee8..0ec5f20676c82ba2e03723bc36bbfea282d5858a 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   DXE capsule report related function.\r
 \r
-  Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include <IndustryStandard/WindowsUxCapsule.h>\r
 \r
+/**\r
+  This routine is called to clear CapsuleOnDisk Relocation Info variable.\r
+  Total Capsule On Disk length is recorded in this variable\r
+\r
+  @retval EFI_SUCCESS   Capsule On Disk flags are cleared\r
+\r
+**/\r
+EFI_STATUS\r
+CoDClearCapsuleRelocationInfo(\r
+  VOID\r
+  );\r
+\r
 /**\r
   Get current capsule last variable index.\r
 \r
@@ -174,6 +186,7 @@ RecordCapsuleStatusVariable (
   @param[in] PayloadIndex   FMP payload index\r
   @param[in] ImageHeader    FMP image header\r
   @param[in] FmpDevicePath  DevicePath associated with the FMP producer\r
+  @param[in] CapFileName    Capsule file name\r
 \r
   @retval EFI_SUCCESS          The capsule status variable is recorded.\r
   @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.\r
@@ -184,7 +197,8 @@ RecordFmpCapsuleStatusVariable (
   IN EFI_STATUS                                    CapsuleStatus,\r
   IN UINTN                                         PayloadIndex,\r
   IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *ImageHeader,\r
-  IN EFI_DEVICE_PATH_PROTOCOL                      *FmpDevicePath OPTIONAL\r
+  IN EFI_DEVICE_PATH_PROTOCOL                      *FmpDevicePath, OPTIONAL\r
+  IN CHAR16                                        *CapFileName    OPTIONAL\r
   )\r
 {\r
   EFI_CAPSULE_RESULT_VARIABLE_HEADER  *CapsuleResultVariableHeader;\r
@@ -194,8 +208,11 @@ RecordFmpCapsuleStatusVariable (
   UINTN                               CapsuleResultVariableSize;\r
   CHAR16                              *DevicePathStr;\r
   UINTN                               DevicePathStrSize;\r
+  UINTN                               CapFileNameSize;\r
+\r
+  DevicePathStr   = NULL;\r
+  CapFileNameSize = sizeof(CHAR16);\r
 \r
-  DevicePathStr = NULL;\r
   if (FmpDevicePath != NULL) {\r
     DevicePathStr = ConvertDevicePathToText (FmpDevicePath, FALSE, FALSE);\r
   }\r
@@ -204,10 +221,16 @@ RecordFmpCapsuleStatusVariable (
   } else {\r
     DevicePathStrSize = sizeof(CHAR16);\r
   }\r
+\r
+  if (CapFileName != NULL) {\r
+    CapFileNameSize = StrSize(CapFileName);\r
+  }\r
+\r
   //\r
-  // Allocate zero CHAR16 for CapsuleFileName.\r
+  // Allocate room for CapsuleFileName.\r
   //\r
-  CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) + DevicePathStrSize;\r
+  CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + CapFileNameSize + DevicePathStrSize;\r
+\r
   CapsuleResultVariable     = AllocateZeroPool (CapsuleResultVariableSize);\r
   if (CapsuleResultVariable == NULL) {\r
     return EFI_OUT_OF_RESOURCES;\r
@@ -225,8 +248,13 @@ RecordFmpCapsuleStatusVariable (
   CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex;\r
   CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex;\r
   CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId);\r
+\r
+  if (CapFileName != NULL) {\r
+    CopyMem((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP), CapFileName, CapFileNameSize);\r
+  }\r
+\r
   if (DevicePathStr != NULL) {\r
-    CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16), DevicePathStr, DevicePathStrSize);\r
+    CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + CapFileNameSize, DevicePathStr, DevicePathStrSize);\r
     FreePool (DevicePathStr);\r
     DevicePathStr = NULL;\r
   }\r
@@ -400,6 +428,31 @@ InitCapsuleUpdateVariable (
   }\r
 }\r
 \r
+/**\r
+  Initialize capsule relocation info variable.\r
+**/\r
+VOID\r
+InitCapsuleRelocationInfo (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;\r
+\r
+  CoDClearCapsuleRelocationInfo();\r
+\r
+  //\r
+  // Unlock Capsule On Disk relocation Info variable only when Capsule On Disk flag is enabled\r
+  //\r
+  if (!CoDCheckCapsuleOnDiskFlag()) {\r
+    Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = VariableLock->RequestToLock (VariableLock, COD_RELOCATION_INFO_VAR_NAME, &gEfiCapsuleVendorGuid);\r
+      ASSERT_EFI_ERROR (Status);\r
+    }\r
+  }\r
+}\r
+\r
 /**\r
   Initialize capsule related variables.\r
 **/\r
@@ -411,6 +464,8 @@ InitCapsuleVariable (
   InitCapsuleUpdateVariable();\r
   InitCapsuleMaxVariable();\r
   InitCapsuleLastVariable();\r
+  InitCapsuleRelocationInfo();\r
+\r
   //\r
   // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"\r
   // to check status and delete them.\r
index dc67fcbe2082e5755f78a72a615bb97687f1bd15..b09631830ffbbddfb21bec41becd1ed0fb8fba69 100644 (file)
@@ -3,7 +3,7 @@
   Dummy function for runtime module, because CapsuleDxeRuntime\r
   does not need record capsule status variable.\r
 \r
-  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
@@ -39,6 +39,7 @@ RecordCapsuleStatusVariable (
   @param[in] PayloadIndex   FMP payload index\r
   @param[in] ImageHeader    FMP image header\r
   @param[in] FmpDevicePath  DevicePath associated with the FMP producer\r
+  @param[in] CapFileName    Capsule file name\r
 \r
   @retval EFI_SUCCESS          The capsule status variable is recorded.\r
   @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.\r
@@ -49,7 +50,8 @@ RecordFmpCapsuleStatusVariable (
   IN EFI_STATUS                                    CapsuleStatus,\r
   IN UINTN                                         PayloadIndex,\r
   IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER  *ImageHeader,\r
-  IN EFI_DEVICE_PATH_PROTOCOL                      *FmpDevicePath OPTIONAL\r
+  IN EFI_DEVICE_PATH_PROTOCOL                      *FmpDevicePath, OPTIONAL\r
+  IN CHAR16                                        *CapFileName    OPTIONAL\r
   )\r
 {\r
   return EFI_UNSUPPORTED;\r
index 2c93e6870023333afd786c29e85850febb56ec95..bf56f4623f80f3a2895a2f894df6843f38170d13 100644 (file)
@@ -3,7 +3,7 @@
 #\r
 #  Capsule library instance for DXE_RUNTIME_DRIVER module types.\r
 #\r
-#  Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
+#  Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
 #  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 #\r
 ##\r
@@ -68,6 +68,7 @@
   gEfiEndOfDxeEventGroupGuid              ## CONSUMES ## Event\r
   gEfiEventReadyToBootGuid                ## CONSUMES ## Event\r
   gEfiEventVirtualAddressChangeGuid       ## CONSUMES ## Event\r
+  gEdkiiCapsuleOnDiskNameGuid             ## SOMETIMES_CONSUMES ## GUID\r
 \r
 [Depex]\r
   gEfiVariableWriteArchProtocolGuid\r
index 06a1abe16b0f0a17daf08c08f3b9c6e9b42843ec..55985abd78415a9431deee9812907911baf66104 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Null Dxe Capsule Library instance does nothing and returns unsupport status.\r
 \r
-Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>\r
 SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
@@ -85,3 +85,86 @@ ProcessCapsules (
   return EFI_UNSUPPORTED;\r
 }\r
 \r
+\r
+/**\r
+  This routine is called to check if CapsuleOnDisk flag in OsIndications Variable\r
+  is enabled.\r
+\r
+  @retval TRUE     Flag is enabled\r
+  @retval FALSE    Flag is not enabled\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+CoDCheckCapsuleOnDiskFlag(\r
+  VOID\r
+  )\r
+{\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.\r
+\r
+  @retval EFI_SUCCESS   All Capsule On Disk flags are cleared\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDClearCapsuleOnDiskFlag(\r
+  VOID\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Relocate Capsule on Disk from EFI system partition.\r
+\r
+  Two solution to deliver Capsule On Disk:\r
+  Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().\r
+  Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage\r
+  device with BlockIo protocol.\r
+\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  Side Effects:\r
+    Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.\r
+    Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file\r
+  systems of the relocation device will be corrupted.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+  @retval EFI_SUCCESS   Capsule on Disk images are successfully relocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRelocateCapsule(\r
+  UINTN     MaxRetry\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Remove the temp file from the root of EFI System Partition.\r
+  Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+  Function will stall 100ms between each retry.\r
+\r
+  @param[in]    MaxRetry             Max Connection Retry. Stall 100ms between each connection try to ensure\r
+                                     devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+  @retval EFI_SUCCESS   Remove the temp file successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRemoveTempFile (\r
+  UINTN    MaxRetry\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r