]> git.proxmox.com Git - mirror_edk2.git/commitdiff
Add capsule > 4GB support. When capsule data is put above 4GB, IA32 PEI transfers...
authorli-elvin <li-elvin@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 2 Sep 2011 11:34:35 +0000 (11:34 +0000)
committerli-elvin <li-elvin@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 2 Sep 2011 11:34:35 +0000 (11:34 +0000)
Signed-off-by: li-elvin
Reviewed-by: lgao4, mdkinney
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12264 6f19259b-4bc3-4df7-8a09-765794883524

13 files changed:
MdeModulePkg/Include/Guid/CapsuleVendor.h
MdeModulePkg/MdeModulePkg.dec
MdeModulePkg/Universal/CapsulePei/Capsule.h
MdeModulePkg/Universal/CapsulePei/CapsulePei.inf
MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf [new file with mode: 0644]
MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c [new file with mode: 0644]
MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h [new file with mode: 0644]
MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c [new file with mode: 0644]
MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c
MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c [new file with mode: 0644]
MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c [new file with mode: 0644]

index 0a1ebd94e618f07dc9df453135fe6e1132d2d3f9..15ca1055b6dc91d9de495041844d3cf5a101cde5 100644 (file)
@@ -9,7 +9,7 @@
   @par Note: EDKII implementation of capsule updating has discarded this capsule GUID HOB data\r
              structure and used one UEFI Capsule HOB (defined in PI Specification 1.2) instead.\r
 \r
-Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
 This program and the accompanying materials are licensed and made available under \r
 the terms and conditions of the BSD License that accompanies this distribution.  \r
 The full text of the license may be found at\r
@@ -48,6 +48,18 @@ typedef struct {
   UINT32 Length;                     ///< Length of capsule data.\r
 } CAPSULE_HOB_INFO;\r
 \r
+//\r
+// The variable describes the long mode buffer used by IA32 Capsule PEIM\r
+// to call X64 CapsuleCoalesce code to handle >4GB capsule blocks.\r
+//\r
+#define EFI_CAPSULE_LONG_MODE_BUFFER_NAME L"CapsuleLongModeBuffer"\r
+\r
+typedef struct {\r
+  EFI_PHYSICAL_ADDRESS   PageTableAddress;\r
+  EFI_PHYSICAL_ADDRESS   StackBaseAddress;\r
+  UINT64                 StackSize;\r
+} EFI_CAPSULE_LONG_MODE_BUFFER;\r
+\r
 extern EFI_GUID gEfiCapsuleVendorGuid;\r
 \r
 #endif // #ifndef _EFI_CAPSULE_VENDOR_GUID_H_\r
index ac345655ca27b36d39166c7acc2b111a0a87391b..55626812f2e350ac167f3347861a2e7bfa0b9afa 100644 (file)
   ## FFS filename to find the ACPI tables\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile|{ 0x25, 0x4e, 0x37, 0x7e, 0x01, 0x8e, 0xee, 0x4f, 0x87, 0xf2, 0x39, 0xc, 0x23, 0xc6, 0x6, 0xcd }|VOID*|0x30000016\r
 \r
+  ## FFS filename to find the capsule coalesce image.\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleCoalesceFile|{ 0xA6, 0xE4, 0xFD, 0xF7, 0x4C, 0x29, 0x3c, 0x49, 0xB5, 0x0F, 0x97, 0x34, 0x55, 0x3B, 0xB7, 0x57 }|VOID*|0x30000017\r
+\r
   ## Single root I/O virtualization virtual function memory BAR alignment\r
   #  BITN set indicates 2 of n+12 power\r
   #  BIT0 set indicates 4KB alignment\r
   #  script node created in runtime phase.\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptRuntimeTableReservePageNumber|0x2|UINT16|0x0001005C\r
 \r
+  ## The PCD is used to specify the stack size when capsule IA32 PEI transfers to long mode in PEI phase.\r
+  #  The default size 32K. When changing the value of this PCD, the platform developer should\r
+  #  make sure the memory size is large enough to meet capsule PEI requiremnt in capsule update path.\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCapsulePeiLongModeStackSize|0x8000|UINT32|0x0001005C\r
+\r
 [PcdsPatchableInModule]\r
   ## Specify  memory size with page number for PEI code when \r
   #  the feature of Loading Module at Fixed Address is enabled\r
index 2a042f288d6f869843d50c779ad19ae8d82a7ca0..8d8658a0c115ab04e97b64db569b970eca088ad0 100644 (file)
@@ -20,7 +20,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <Uefi/UefiSpec.h>\r
 \r
 #include <Ppi/Capsule.h>\r
-\r
+#include <Ppi/LoadFile.h>\r
 #include <Ppi/ReadOnlyVariable2.h>\r
 #include <Guid/CapsuleVendor.h>\r
 \r
@@ -31,25 +31,71 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <Library/HobLib.h>\r
 #include <Library/PeiServicesTablePointerLib.h>\r
 #include <Library/PrintLib.h>\r
+#include <Library/PeCoffLib.h>\r
+#include <Library/PeCoffGetEntryPointLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/ReportStatusCodeLib.h>\r
+#include <IndustryStandard/PeImage.h>\r
+#include "Common/CommonHeader.h"\r
+\r
+#pragma pack(1)\r
 \r
 //\r
-// We want to avoid using memory at 0 for coalescing, so set a\r
-// min address.\r
+// Page-Map Level-4 Offset (PML4) and\r
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB\r
 //\r
-#define MIN_COALESCE_ADDR 0x100000\r
-#define MAX_SUPPORT_CAPSULE_NUM 50\r
+\r
+typedef union {\r
+  struct {\r
+    UINT64  Present:1;                // 0 = Not present in memory, 1 = Present in memory\r
+    UINT64  ReadWrite:1;              // 0 = Read-Only, 1= Read/Write\r
+    UINT64  UserSupervisor:1;         // 0 = Supervisor, 1=User\r
+    UINT64  WriteThrough:1;           // 0 = Write-Back caching, 1=Write-Through caching\r
+    UINT64  CacheDisabled:1;          // 0 = Cached, 1=Non-Cached\r
+    UINT64  Accessed:1;               // 0 = Not accessed, 1 = Accessed (set by CPU)\r
+    UINT64  Reserved:1;               // Reserved\r
+    UINT64  MustBeZero:2;             // Must Be Zero\r
+    UINT64  Available:3;              // Available for use by system software\r
+    UINT64  PageTableBaseAddress:40;  // Page Table Base Address\r
+    UINT64  AvabilableHigh:11;        // Available for use by system software\r
+    UINT64  Nx:1;                     // No Execute bit\r
+  } Bits;\r
+  UINT64    Uint64;\r
+} PAGE_MAP_AND_DIRECTORY_POINTER;\r
 \r
 //\r
-// This capsule PEIM puts its private data at the start of the\r
-// coalesced capsule. Here's the structure definition.\r
+// Page Table Entry 2MB\r
 //\r
-#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'D')\r
+typedef union {\r
+  struct {\r
+    UINT64  Present:1;                // 0 = Not present in memory, 1 = Present in memory\r
+    UINT64  ReadWrite:1;              // 0 = Read-Only, 1= Read/Write\r
+    UINT64  UserSupervisor:1;         // 0 = Supervisor, 1=User\r
+    UINT64  WriteThrough:1;           // 0 = Write-Back caching, 1=Write-Through caching\r
+    UINT64  CacheDisabled:1;          // 0 = Cached, 1=Non-Cached\r
+    UINT64  Accessed:1;               // 0 = Not accessed, 1 = Accessed (set by CPU)\r
+    UINT64  Dirty:1;                  // 0 = Not Dirty, 1 = written by processor on access to page\r
+    UINT64  MustBe1:1;                // Must be 1 \r
+    UINT64  Global:1;                 // 0 = Not global page, 1 = global page TLB not cleared on CR3 write\r
+    UINT64  Available:3;              // Available for use by system software\r
+    UINT64  PAT:1;                    //\r
+    UINT64  MustBeZero:8;             // Must be zero;\r
+    UINT64  PageTableBaseAddress:31;  // Page Table Base Address\r
+    UINT64  AvabilableHigh:11;        // Available for use by system software\r
+    UINT64  Nx:1;                     // 0 = Execute Code, 1 = No Code Execution\r
+  } Bits;\r
+  UINT64    Uint64;\r
+} PAGE_TABLE_ENTRY;\r
 \r
-typedef struct {\r
-  UINT32  Signature;\r
-  UINTN   CapsuleSize;\r
-} EFI_CAPSULE_PEIM_PRIVATE_DATA;\r
+#pragma pack()\r
 \r
-#define CAPSULE_TEST_SIGNATURE SIGNATURE_32('T', 'E', 'S', 'T')\r
+typedef\r
+EFI_STATUS\r
+(*COALESCE_ENTRY) (\r
+  IN EFI_PEI_SERVICES                **PeiServices,\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR    *BlockList,\r
+  IN OUT VOID                        **MemoryBase,\r
+  IN OUT UINTN                       *MemorySize\r
+  );\r
 \r
 #endif\r
index c66e7720ca8491489974bfba6658eaccd90d621c..2599cdbcd12c658f80fa2ee77f7b3acee3887072 100644 (file)
@@ -3,7 +3,7 @@
 #\r
 # Capsule update module supports EFI and UEFI.\r
 #\r
-# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
 #\r
 # This program and the accompanying materials\r
 # are licensed and made available under the terms and conditions\r
@@ -34,6 +34,7 @@
 [Sources]\r
   UefiCapsule.c\r
   Capsule.h\r
+  Common/CapsuleCoalesce.c  \r
 \r
 [Packages]\r
   MdePkg/MdePkg.dec\r
   DebugLib\r
   PeiServicesTablePointerLib\r
   PrintLib\r
+  PeCoffLib\r
+  PeCoffGetEntryPointLib\r
+  PcdLib\r
+  ReportStatusCodeLib\r
 \r
 [Guids]\r
   gEfiCapsuleVendorGuid                         # ALWAYS_CONSUMED\r
 [Ppis]\r
   gEfiPeiReadOnlyVariable2PpiGuid               # PPI ALWAYS_CONSUMED\r
   gPeiCapsulePpiGuid                            # PPI ALWAYS_CONSUMED\r
+  gEfiPeiLoadFilePpiGuid                        # PPI ALWAYS_CONSUMED\r
 \r
+[Pcd]\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleCoalesceFile\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode\r
 \r
 [Depex]\r
   gEfiPeiReadOnlyVariable2PpiGuid\r
diff --git a/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf b/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf
new file mode 100644 (file)
index 0000000..b12d3e5
--- /dev/null
@@ -0,0 +1,48 @@
+## @file\r
+# Component description file for CapsuleX64 module.\r
+#\r
+# The X64 entrypoint to process capsule in long mode.\r
+# This module is built as X64.\r
+#\r
+# Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions\r
+# of the BSD License which accompanies this distribution.  The\r
+# full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = CapsuleX64\r
+  FILE_GUID                      = F7FDE4A6-294C-493c-B50F-9734553BB757\r
+  MODULE_TYPE                    = PEIM\r
+  VERSION_STRING                 = 1.0\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = X64\r
+#\r
+\r
+[Sources]\r
+  X64/X64Entry.c\r
+  Common/CapsuleCoalesce.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  DebugLib\r
+\r
+[Depex]\r
+  FALSE\r
+\r
+\r
diff --git a/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c b/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c
new file mode 100644 (file)
index 0000000..76c719a
--- /dev/null
@@ -0,0 +1,1104 @@
+/** @file\r
+  The logic to process capsule.\r
+\r
+Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+#include <PiPei.h>\r
+\r
+#include <Guid/CapsuleVendor.h>\r
+\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/BaseLib.h>\r
+\r
+#define MIN_COALESCE_ADDR                     (1024 * 1024)\r
+#define MAX_SUPPORT_CAPSULE_NUM               50\r
+\r
+#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'D')\r
+\r
+typedef struct {\r
+  UINT32  Signature;\r
+  UINT32  CapsuleSize;\r
+} EFI_CAPSULE_PEIM_PRIVATE_DATA;\r
+\r
+/**\r
+  Given a pointer to the capsule block list, info on the available system\r
+  memory, and the size of a buffer, find a free block of memory where a\r
+  buffer of the given size can be copied to safely.\r
+\r
+  @param BlockList   Pointer to head of capsule block descriptors\r
+  @param MemBase     Pointer to the base of memory in which we want to find free space\r
+  @param MemSize     The size of the block of memory pointed to by MemBase\r
+  @param DataSize    How big a free block we want to find\r
+\r
+  @return A pointer to a memory block of at least DataSize that lies somewhere \r
+          between MemBase and (MemBase + MemSize). The memory pointed to does not\r
+          contain any of the capsule block descriptors or capsule blocks pointed to\r
+          by the BlockList.\r
+\r
+**/\r
+UINT8 *\r
+FindFreeMem (\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR     *BlockList,\r
+  UINT8                            *MemBase,\r
+  UINTN                             MemSize,\r
+  UINTN                             DataSize\r
+  );\r
+\r
+/**\r
+  Check the integrity of the capsule descriptors.\r
+\r
+  @param BlockList    Pointer to the capsule descriptors\r
+\r
+  @retval NULL           BlockList is not valid.\r
+  @retval LastBlockDesc  Last one Block in BlockList\r
+\r
+**/\r
+EFI_CAPSULE_BLOCK_DESCRIPTOR *\r
+ValidateCapsuleIntegrity (\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR    *BlockList\r
+  );\r
+\r
+/**\r
+  The capsule block descriptors may be fragmented and spread all over memory.\r
+  To simplify the coalescing of capsule blocks, first coalesce all the\r
+  capsule block descriptors low in memory.\r
+\r
+  The descriptors passed in can be fragmented throughout memory. Here\r
+  they are relocated into memory to turn them into a contiguous (null\r
+  terminated) array.\r
+\r
+  @param PeiServices pointer to PEI services table\r
+  @param BlockList   pointer to the capsule block descriptors\r
+  @param MemBase     base of system memory in which we can work\r
+  @param MemSize     size of the system memory pointed to by MemBase\r
+\r
+  @retval NULL    could not relocate the descriptors\r
+  @retval Pointer to the base of the successfully-relocated block descriptors. \r
+\r
+**/\r
+EFI_CAPSULE_BLOCK_DESCRIPTOR *\r
+RelocateBlockDescriptors (\r
+  IN EFI_PEI_SERVICES                  **PeiServices,\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR      *BlockList,\r
+  IN UINT8                             *MemBase,\r
+  IN UINTN                             MemSize\r
+  );\r
+\r
+/**\r
+  Check every capsule header.\r
+\r
+  @param CapsuleHeader   The pointer to EFI_CAPSULE_HEADER\r
+\r
+  @retval FALSE  Capsule is OK\r
+  @retval TRUE   Capsule is corrupted \r
+\r
+**/\r
+BOOLEAN\r
+IsCapsuleCorrupted (\r
+  IN EFI_CAPSULE_HEADER       *CapsuleHeader\r
+  );\r
+\r
+/**\r
+  Determine if two buffers overlap in memory.\r
+\r
+  @param Buff1   pointer to first buffer\r
+  @param Size1   size of Buff1\r
+  @param Buff2   pointer to second buffer\r
+  @param Size2   size of Buff2\r
+\r
+  @retval TRUE    Buffers overlap in memory.\r
+  @retval FALSE   Buffer doesn't overlap.\r
+\r
+**/\r
+BOOLEAN\r
+IsOverlapped (\r
+  UINT8     *Buff1,\r
+  UINTN     Size1,\r
+  UINT8     *Buff2,\r
+  UINTN     Size2\r
+  );\r
+\r
+/**\r
+  Given a pointer to a capsule block descriptor, traverse the list to figure\r
+  out how many legitimate descriptors there are, and how big the capsule it\r
+  refers to is.\r
+\r
+  @param Desc            Pointer to the capsule block descriptors\r
+                         NumDescriptors  - optional pointer to where to return the number of descriptors\r
+                         CapsuleSize     - optional pointer to where to return the capsule size\r
+  @param NumDescriptors  Optional pointer to where to return the number of descriptors\r
+  @param CapsuleSize     Optional pointer to where to return the capsule size\r
+\r
+  @retval EFI_NOT_FOUND   No descriptors containing data in the list\r
+  @retval EFI_SUCCESS     Return data is valid\r
+\r
+**/\r
+EFI_STATUS\r
+GetCapsuleInfo (\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR   *Desc,\r
+  IN OUT UINTN                      *NumDescriptors OPTIONAL,\r
+  IN OUT UINTN                      *CapsuleSize OPTIONAL\r
+  );\r
+\r
+/**\r
+  Given a pointer to the capsule block list, info on the available system\r
+  memory, and the size of a buffer, find a free block of memory where a\r
+  buffer of the given size can be copied to safely.\r
+\r
+  @param BlockList   Pointer to head of capsule block descriptors\r
+  @param MemBase     Pointer to the base of memory in which we want to find free space\r
+  @param MemSize     The size of the block of memory pointed to by MemBase\r
+  @param DataSize    How big a free block we want to find\r
+\r
+  @return A pointer to a memory block of at least DataSize that lies somewhere \r
+          between MemBase and (MemBase + MemSize). The memory pointed to does not\r
+          contain any of the capsule block descriptors or capsule blocks pointed to\r
+          by the BlockList.\r
+\r
+**/\r
+UINT8 *\r
+FindFreeMem (\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR      *BlockList,\r
+  UINT8                             *MemBase,\r
+  UINTN                             MemSize,\r
+  UINTN                             DataSize\r
+  )\r
+{\r
+  UINTN                           Size;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR    *CurrDesc;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR    *TempDesc;\r
+  UINT8                           *MemEnd;\r
+  BOOLEAN                         Failed;\r
+\r
+  //\r
+  // Need at least enough to copy the data to at the end of the buffer, so\r
+  // say the end is less the data size for easy comparisons here.\r
+  //\r
+  MemEnd    = MemBase + MemSize - DataSize;\r
+  CurrDesc  = BlockList;\r
+  //\r
+  // Go through all the descriptor blocks and see if any obstruct the range\r
+  //\r
+  while (CurrDesc != NULL) {\r
+    //\r
+    // Get the size of this block list and see if it's in the way\r
+    //\r
+    Failed    = FALSE;\r
+    TempDesc  = CurrDesc;\r
+    Size      = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
+    while (TempDesc->Length != 0) {\r
+      Size += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
+      TempDesc++;\r
+    }\r
+\r
+    if (IsOverlapped (MemBase, DataSize, (UINT8 *) CurrDesc, Size)) {\r
+      //\r
+      // Set our new base to the end of this block list and start all over\r
+      //\r
+      MemBase   = (UINT8 *) CurrDesc + Size;\r
+      CurrDesc  = BlockList;\r
+      if (MemBase > MemEnd) {\r
+        return NULL;\r
+      }\r
+\r
+      Failed = TRUE;\r
+    }\r
+    //\r
+    // Now go through all the blocks and make sure none are in the way\r
+    //\r
+    while ((CurrDesc->Length != 0) && (!Failed)) {\r
+      if (IsOverlapped (MemBase, DataSize, (UINT8 *) (UINTN) CurrDesc->Union.DataBlock, (UINTN) CurrDesc->Length)) {\r
+        //\r
+        // Set our new base to the end of this block and start all over\r
+        //\r
+        Failed    = TRUE;\r
+        MemBase   = (UINT8 *) ((UINTN) CurrDesc->Union.DataBlock) + CurrDesc->Length;\r
+        CurrDesc  = BlockList;\r
+        if (MemBase > MemEnd) {\r
+          return NULL;\r
+        }\r
+      }\r
+      CurrDesc++;\r
+    }\r
+    //\r
+    // Normal continuation -- jump to next block descriptor list\r
+    //\r
+    if (!Failed) {\r
+      CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) CurrDesc->Union.ContinuationPointer;\r
+    }\r
+  }\r
+  return MemBase;\r
+}\r
+\r
+/**\r
+  Check the integrity of the capsule descriptors.\r
+\r
+  @param BlockList    Pointer to the capsule descriptors\r
+\r
+  @retval NULL           BlockList is not valid.\r
+  @retval LastBlockDesc  Last one Block in BlockList\r
+\r
+**/\r
+EFI_CAPSULE_BLOCK_DESCRIPTOR *\r
+ValidateCapsuleIntegrity (\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR    *BlockList\r
+  )\r
+{\r
+  EFI_CAPSULE_HEADER             *CapsuleHeader;\r
+  UINT64                         CapsuleSize;\r
+  UINT32                         CapsuleCount;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *Ptr;\r
+\r
+  //\r
+  // Go through the list to look for inconsistencies. Check for:\r
+  //   * misaligned block descriptors.\r
+  //   * The first capsule header guid\r
+  //   * The first capsule header flag\r
+  //   * Data + Length < Data (wrap)\r
+  CapsuleSize  = 0;\r
+  CapsuleCount = 0;\r
+  Ptr = BlockList;\r
+  while ((Ptr->Length != 0) || (Ptr->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
+    //\r
+    // Make sure the descriptor is aligned at UINT64 in memory\r
+    //\r
+    if ((UINTN) Ptr & 0x07) {\r
+      DEBUG ((EFI_D_ERROR, "BlockList address failed alignment check\n"));\r
+      return NULL;\r
+    }\r
+\r
+    if (Ptr->Length == 0) {\r
+      //\r
+      // Descriptor points to another list of block descriptors somewhere\r
+      // else.\r
+      //\r
+      Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Ptr->Union.ContinuationPointer;\r
+    } else {\r
+      //\r
+      //To enhance the reliability of check-up, the first capsule's header is checked here.\r
+      //More reliabilities check-up will do later.\r
+      //\r
+      if (CapsuleSize == 0) {\r
+        //\r
+        //Move to the first capsule to check its header.\r
+        //\r
+        CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Ptr->Union.DataBlock);\r
+        if (IsCapsuleCorrupted (CapsuleHeader)) {\r
+          return NULL;\r
+        }\r
+        CapsuleCount ++;\r
+        CapsuleSize = CapsuleHeader->CapsuleImageSize;\r
+      } else {\r
+        if (CapsuleSize >= Ptr->Length) {\r
+          CapsuleSize = CapsuleSize - Ptr->Length;\r
+        } else {\r
+          CapsuleSize = 0;\r
+        }\r
+      }\r
+      //\r
+      // Move to next BLOCK descriptor\r
+      //\r
+      Ptr++;\r
+    }\r
+  }\r
+\r
+  if (CapsuleCount == 0) {\r
+    //\r
+    // No any capsule is found in BlockList.\r
+    //\r
+    return NULL;\r
+  }\r
+\r
+  return Ptr;\r
+}\r
+\r
+/**\r
+  The capsule block descriptors may be fragmented and spread all over memory.\r
+  To simplify the coalescing of capsule blocks, first coalesce all the\r
+  capsule block descriptors low in memory.\r
+\r
+  The descriptors passed in can be fragmented throughout memory. Here\r
+  they are relocated into memory to turn them into a contiguous (null\r
+  terminated) array.\r
+\r
+  @param PeiServices pointer to PEI services table\r
+  @param BlockList   pointer to the capsule block descriptors\r
+  @param MemBase     base of system memory in which we can work\r
+  @param MemSize     size of the system memory pointed to by MemBase\r
+\r
+  @retval NULL    could not relocate the descriptors\r
+  @retval Pointer to the base of the successfully-relocated block descriptors. \r
+\r
+**/\r
+EFI_CAPSULE_BLOCK_DESCRIPTOR  *\r
+RelocateBlockDescriptors (\r
+  IN EFI_PEI_SERVICES                   **PeiServices,\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR       *BlockList,\r
+  IN UINT8                              *MemBase,\r
+  IN UINTN                              MemSize\r
+  )\r
+{\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *NewBlockList;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *CurrBlockDescHead;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *TempBlockDesc;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *PrevBlockDescTail;\r
+  UINTN                          NumDescriptors;\r
+  UINTN                          BufferSize;\r
+  UINT8                          *RelocBuffer;\r
+  UINTN                          BlockListSize;\r
+  //\r
+  // Get the info on the blocks and descriptors. Since we're going to move\r
+  // the descriptors low in memory, adjust the base/size values accordingly here.\r
+  // GetCapsuleInfo() returns the number of legit descriptors, so add one for\r
+  // a terminator.\r
+  //\r
+  if (GetCapsuleInfo (BlockList, &NumDescriptors, NULL) != EFI_SUCCESS) {\r
+    return NULL;\r
+  }\r
+\r
+  NumDescriptors++;\r
+  BufferSize    = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
+  NewBlockList  = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) MemBase;\r
+  if (MemSize < BufferSize) {\r
+    return NULL;\r
+  }\r
+\r
+  MemSize -= BufferSize;\r
+  MemBase += BufferSize;\r
+  //\r
+  // Go through all the blocks and make sure none are in the way\r
+  //\r
+  TempBlockDesc = BlockList;\r
+  while (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {\r
+    if (TempBlockDesc->Length == 0) {\r
+      //\r
+      // Next block of descriptors\r
+      //\r
+      TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) TempBlockDesc->Union.ContinuationPointer;\r
+    } else {\r
+      //\r
+      // If the capsule data pointed to by this descriptor is in the way,\r
+      // move it.\r
+      //\r
+      if (IsOverlapped (\r
+            (UINT8 *) NewBlockList,\r
+            BufferSize,\r
+            (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,\r
+            (UINTN) TempBlockDesc->Length\r
+            )) {\r
+        //\r
+        // Relocate the block\r
+        //\r
+        RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, (UINTN) TempBlockDesc->Length);\r
+        if (RelocBuffer == NULL) {\r
+          return NULL;\r
+        }\r
+\r
+        CopyMem ((VOID *) RelocBuffer, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);\r
+        TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;\r
+\r
+        DEBUG ((EFI_D_INFO, "Capsule relocate descriptors from/to/size  0x%X 0x%X 0x%X\n", (UINT32)(UINTN)TempBlockDesc->Union.DataBlock, (UINT32)(UINTN)RelocBuffer, (UINT32)(UINTN)TempBlockDesc->Length));\r
+      }\r
+    }\r
+    TempBlockDesc++;\r
+  }\r
+  //\r
+  // Now go through all the block descriptors to make sure that they're not\r
+  // in the memory region we want to copy them to.\r
+  //\r
+  CurrBlockDescHead = BlockList;\r
+  PrevBlockDescTail = NULL;\r
+  while ((CurrBlockDescHead != NULL) && (CurrBlockDescHead->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
+    //\r
+    // Get the size of this list then see if it overlaps our low region\r
+    //\r
+    TempBlockDesc = CurrBlockDescHead;\r
+    BlockListSize = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
+    while (TempBlockDesc->Length != 0) {\r
+      BlockListSize += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
+      TempBlockDesc++;\r
+    }\r
+\r
+    if (IsOverlapped (\r
+          (UINT8 *) NewBlockList,\r
+          BufferSize,\r
+          (UINT8 *) CurrBlockDescHead,\r
+          BlockListSize\r
+          )) {\r
+      //\r
+      // Overlaps, so move it out of the way\r
+      //\r
+      RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, BlockListSize);\r
+      if (RelocBuffer == NULL) {\r
+        return NULL;\r
+      }\r
+      CopyMem ((VOID *) RelocBuffer, (VOID *) CurrBlockDescHead, BlockListSize);\r
+      DEBUG ((EFI_D_INFO, "Capsule reloc descriptor block #2\n"));\r
+      //\r
+      // Point the previous block's next point to this copied version. If\r
+      // the tail pointer is null, then this is the first descriptor block.\r
+      //\r
+      if (PrevBlockDescTail == NULL) {\r
+        BlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) RelocBuffer;\r
+      } else {\r
+        PrevBlockDescTail->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;\r
+      }\r
+    }\r
+    //\r
+    // Save our new tail and jump to the next block list\r
+    //\r
+    PrevBlockDescTail = TempBlockDesc;\r
+    CurrBlockDescHead = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) TempBlockDesc->Union.ContinuationPointer;\r
+  }\r
+  //\r
+  // Cleared out low memory. Now copy the descriptors down there.\r
+  //\r
+  TempBlockDesc     = BlockList;\r
+  CurrBlockDescHead = NewBlockList;\r
+  while ((TempBlockDesc != NULL) && (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
+    if (TempBlockDesc->Length != 0) {\r
+      CurrBlockDescHead->Union.DataBlock = TempBlockDesc->Union.DataBlock;\r
+      CurrBlockDescHead->Length = TempBlockDesc->Length;\r
+      CurrBlockDescHead++;\r
+      TempBlockDesc++;\r
+    } else {\r
+      TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) TempBlockDesc->Union.ContinuationPointer;\r
+    }\r
+  }\r
+  //\r
+  // Null terminate\r
+  //\r
+  CurrBlockDescHead->Union.ContinuationPointer   = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL;\r
+  CurrBlockDescHead->Length = 0;\r
+  return NewBlockList;\r
+}\r
+\r
+/**\r
+  Determine if two buffers overlap in memory.\r
+\r
+  @param Buff1   pointer to first buffer\r
+  @param Size1   size of Buff1\r
+  @param Buff2   pointer to second buffer\r
+  @param Size2   size of Buff2\r
+\r
+  @retval TRUE    Buffers overlap in memory.\r
+  @retval FALSE   Buffer doesn't overlap.\r
+\r
+**/\r
+BOOLEAN\r
+IsOverlapped (\r
+  UINT8     *Buff1,\r
+  UINTN     Size1,\r
+  UINT8     *Buff2,\r
+  UINTN     Size2\r
+  )\r
+{\r
+  //\r
+  // If buff1's end is less than the start of buff2, then it's ok.\r
+  // Also, if buff1's start is beyond buff2's end, then it's ok.\r
+  //\r
+  if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Given a pointer to a capsule block descriptor, traverse the list to figure\r
+  out how many legitimate descriptors there are, and how big the capsule it\r
+  refers to is.\r
+\r
+  @param Desc            Pointer to the capsule block descriptors\r
+                         NumDescriptors  - optional pointer to where to return the number of descriptors\r
+                         CapsuleSize     - optional pointer to where to return the capsule size\r
+  @param NumDescriptors  Optional pointer to where to return the number of descriptors\r
+  @param CapsuleSize     Optional pointer to where to return the capsule size\r
+\r
+  @retval EFI_NOT_FOUND   No descriptors containing data in the list\r
+  @retval EFI_SUCCESS     Return data is valid\r
+\r
+**/\r
+EFI_STATUS\r
+GetCapsuleInfo (\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR   *Desc,\r
+  IN OUT UINTN                      *NumDescriptors OPTIONAL,\r
+  IN OUT UINTN                      *CapsuleSize OPTIONAL\r
+  )\r
+{\r
+  UINTN Count;\r
+  UINTN Size;\r
+\r
+  Count = 0;\r
+  Size  = 0;\r
+\r
+  while (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {\r
+    if (Desc->Length == 0) {\r
+      //\r
+      // Descriptor points to another list of block descriptors somewhere\r
+      //\r
+      Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Desc->Union.ContinuationPointer;\r
+    } else {\r
+      Size += (UINTN) Desc->Length;\r
+      Count++;\r
+      Desc++;\r
+    }\r
+  }\r
+  //\r
+  // If no descriptors, then fail\r
+  //\r
+  if (Count == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (NumDescriptors != NULL) {\r
+    *NumDescriptors = Count;\r
+  }\r
+\r
+  if (CapsuleSize != NULL) {\r
+    *CapsuleSize = Size;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check every capsule header.\r
+\r
+  @param CapsuleHeader   The pointer to EFI_CAPSULE_HEADER\r
+\r
+  @retval FALSE  Capsule is OK\r
+  @retval TRUE   Capsule is corrupted \r
+\r
+**/\r
+BOOLEAN\r
+IsCapsuleCorrupted (\r
+  IN EFI_CAPSULE_HEADER       *CapsuleHeader\r
+  )\r
+{\r
+  //\r
+  //A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET.\r
+  //\r
+  if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {\r
+    return TRUE;\r
+  }\r
+  //\r
+  //Make sure the flags combination is supported by the platform.\r
+  //\r
+  if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {\r
+    return TRUE;\r
+  }\r
+  if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Try to verify the integrity of a capsule test pattern before the\r
+  capsule gets coalesced. This can be useful in narrowing down\r
+  where capsule data corruption occurs.\r
+\r
+  The test pattern mode fills in memory with a counting UINT32 value. \r
+  If the capsule is not divided up in a multiple of 4-byte blocks, then\r
+  things get messy doing the check. Therefore there are some cases\r
+  here where we just give up and skip the pre-coalesce check.\r
+\r
+  @param PeiServices  PEI services table\r
+  @param Desc         Pointer to capsule descriptors\r
+**/\r
+VOID\r
+CapsuleTestPatternPreCoalesce (\r
+  IN EFI_PEI_SERVICES              **PeiServices,\r
+  IN EFI_CAPSULE_BLOCK_DESCRIPTOR  *Desc\r
+  )\r
+{\r
+  UINT32  *TestPtr;\r
+  UINT32  TestCounter;\r
+  UINT32  TestSize;\r
+  //\r
+  // Find first data descriptor\r
+  //\r
+  while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
+    Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Desc->Union.ContinuationPointer;\r
+  }\r
+\r
+  if (Desc->Union.ContinuationPointer == 0) {\r
+    return ;\r
+  }\r
+  //\r
+  // First one better be long enough to at least hold the test signature\r
+  //\r
+  if (Desc->Length < sizeof (UINT32)) {\r
+    DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #1\n"));\r
+    return ;\r
+  }\r
+\r
+  TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;\r
+  //\r
+  // 0x54534554 "TEST"\r
+  //\r
+  if (*TestPtr != 0x54534554) {\r
+    return ;\r
+  }\r
+\r
+  TestCounter = 0;\r
+  TestSize    = (UINT32) Desc->Length - 2 * sizeof (UINT32);\r
+  //\r
+  // Skip over the signature and the size fields in the pattern data header\r
+  //\r
+  TestPtr += 2;\r
+  while (1) {\r
+    if ((TestSize & 0x03) != 0) {\r
+      DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #2\n"));\r
+      return ;\r
+    }\r
+\r
+    while (TestSize > 0) {\r
+      if (*TestPtr != TestCounter) {\r
+        DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce failed data corruption check\n"));\r
+        return ;\r
+      }\r
+\r
+      TestSize -= sizeof (UINT32);\r
+      TestCounter++;\r
+      TestPtr++;\r
+    }\r
+    Desc++;\r
+    while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
+      Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Desc->Union.ContinuationPointer;\r
+    }\r
+\r
+    if (Desc->Union.ContinuationPointer == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {\r
+      return ;\r
+    }\r
+    TestSize = (UINT32) Desc->Length;\r
+    TestPtr  = (UINT32 *) (UINTN) Desc->Union.DataBlock;\r
+  }\r
+}\r
+\r
+/**\r
+  Checks for the presence of capsule descriptors.\r
+  Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...\r
+\r
+  @param BlockListBuffer            Pointer to the buffer of capsule descriptors variables\r
+  @param BlockDescriptorList        Pointer to the capsule descriptors list\r
+\r
+  @retval EFI_SUCCESS               a valid capsule is present\r
+  @retval EFI_NOT_FOUND             if a valid capsule is not present\r
+**/\r
+EFI_STATUS\r
+BuildCapsuleDescriptors (\r
+  IN  EFI_PHYSICAL_ADDRESS            *BlockListBuffer,\r
+  OUT EFI_CAPSULE_BLOCK_DESCRIPTOR    **BlockDescriptorList \r
+  )\r
+{\r
+  UINTN                            Index;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR     *LastBlock;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR     *TempBlock;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR     *HeadBlock;\r
+\r
+  LastBlock         = NULL;\r
+  HeadBlock         = NULL;\r
+  TempBlock         = NULL;\r
+  Index             = 0;\r
+\r
+  while (BlockListBuffer[Index] != 0) {\r
+    if (Index == 0) {\r
+      //\r
+      // For the first Capsule Image, test integrity of descriptors.\r
+      //\r
+      LastBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]);\r
+      if (LastBlock == NULL) {\r
+        return EFI_NOT_FOUND;\r
+      }\r
+      //\r
+      // Return the base of the block descriptors\r
+      //\r
+      HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index];\r
+    } else {        \r
+      //\r
+      // Test integrity of descriptors.\r
+      //\r
+      TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]);\r
+      if (TempBlock == NULL) {\r
+        return EFI_NOT_FOUND;\r
+      }\r
+      //\r
+      // Combine the different BlockList into single BlockList.\r
+      //\r
+      LastBlock->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)BlockListBuffer[Index];\r
+      LastBlock->Length          = 0;\r
+      LastBlock                  = TempBlock;\r
+    }\r
+    Index ++;\r
+  }\r
+  \r
+  if (HeadBlock != NULL) {\r
+    *BlockDescriptorList = HeadBlock;\r
+    return EFI_SUCCESS;\r
+  }\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+  The function to coalesce a fragmented capsule in memory.\r
+\r
+  Memory Map for coalesced capsule:\r
+  MemBase +   ---->+---------------------------+<-----------+\r
+  MemSize          |    CapsuleOffset[49]      |            |\r
+                   +---------------------------+            |\r
+                   |    ................       |            |\r
+                   +---------------------------+            |\r
+                   |    CapsuleOffset[2]       |            |\r
+                   +---------------------------+            |\r
+                   |    CapsuleOffset[1]       |            |\r
+                   +---------------------------+            |\r
+                   |    CapsuleOffset[0]       |       CapsuleSize     \r
+                   +---------------------------+            |\r
+                   |    CapsuleNumber          |            |\r
+                   +---------------------------+            |\r
+                   |                           |            |       \r
+                   |                           |            |       \r
+                   |    Capsule Image          |            |   \r
+                   |                           |            |       \r
+                   |                           |            |       \r
+                   +---------------------------+            |\r
+                   |    PrivateData            |            |\r
+   DestPtr  ---->  +---------------------------+<-----------+\r
+                   |                           |            |\r
+                   |     FreeMem               |        FreeMemSize\r
+                   |                           |            |\r
+   FreeMemBase --->+---------------------------+<-----------+\r
+                   |    Terminator             |\r
+                   +---------------------------+\r
+                   |    BlockDescriptor n      |\r
+                   +---------------------------+\r
+                   |    .................      |\r
+                   +---------------------------+\r
+                   |    BlockDescriptor 1      |\r
+                   +---------------------------+\r
+                   |    BlockDescriptor 0      |\r
+                   +---------------------------+\r
+                   |    PrivateDataDesc 0      |\r
+      MemBase ---->+---------------------------+<----- BlockList\r
+\r
+  @param PeiServices        General purpose services available to every PEIM.\r
+  @param BlockListBuffer    Point to the buffer of Capsule Descriptor Variables.\r
+  @param MemoryBase         Pointer to the base of a block of memory that we can walk\r
+                            all over while trying to coalesce our buffers.\r
+                            On output, this variable will hold the base address of\r
+                            a coalesced capsule.\r
+  @param MemorySize         Size of the memory region pointed to by MemoryBase.\r
+                            On output, this variable will contain the size of the\r
+                            coalesced capsule.\r
+\r
+  @retval EFI_NOT_FOUND     If we could not find the capsule descriptors.\r
+\r
+  @retval EFI_BUFFER_TOO_SMALL\r
+                            If we could not coalesce the capsule in the memory\r
+                            region provided to us.\r
+\r
+  @retval EFI_SUCCESS       Processed the capsule successfully.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CapsuleDataCoalesce (\r
+  IN EFI_PEI_SERVICES                **PeiServices,\r
+  IN EFI_PHYSICAL_ADDRESS            *BlockListBuffer,\r
+  IN OUT VOID                        **MemoryBase,\r
+  IN OUT UINTN                       *MemorySize\r
+  )\r
+{\r
+  VOID                           *NewCapsuleBase;\r
+  VOID                           *DataPtr;\r
+  UINT8                          CapsuleIndex;\r
+  UINT8                          *FreeMemBase;\r
+  UINT8                          *DestPtr;\r
+  UINT8                          *RelocPtr;\r
+  UINT32                         CapsuleOffset[MAX_SUPPORT_CAPSULE_NUM]; \r
+  UINT32                         *AddDataPtr;\r
+  UINT32                         CapsuleTimes; \r
+  UINT64                         SizeLeft; \r
+  UINT64                         CapsuleImageSize; \r
+  UINTN                          CapsuleSize;\r
+  UINTN                          DescriptorsSize;\r
+  UINTN                          FreeMemSize;\r
+  UINTN                          NumDescriptors;\r
+  BOOLEAN                        IsCorrupted;\r
+  BOOLEAN                        CapsuleBeginFlag;\r
+  EFI_STATUS                     Status;\r
+  EFI_CAPSULE_HEADER             *CapsuleHeader;\r
+  EFI_CAPSULE_PEIM_PRIVATE_DATA  PrivateData;\r
+  EFI_CAPSULE_PEIM_PRIVATE_DATA  *PrivateDataPtr;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *BlockList;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *CurrentBlockDesc;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   *TempBlockDesc;\r
+  EFI_CAPSULE_BLOCK_DESCRIPTOR   PrivateDataDesc[2];\r
+\r
+  CapsuleIndex     = 0;\r
+  SizeLeft         = 0;\r
+  CapsuleTimes     = 0;\r
+  CapsuleImageSize = 0;\r
+  PrivateDataPtr   = NULL;\r
+  AddDataPtr       = NULL;\r
+  CapsuleHeader    = NULL;\r
+  CapsuleBeginFlag = TRUE;\r
+  IsCorrupted      = TRUE;\r
+  CapsuleSize      = 0;\r
+  NumDescriptors   = 0;\r
+  \r
+  //\r
+  // Build capsule descriptors list\r
+  //\r
+  Status = BuildCapsuleDescriptors (BlockListBuffer, &BlockList);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  DEBUG_CODE (\r
+    CapsuleTestPatternPreCoalesce (PeiServices, BlockList);\r
+  );\r
+\r
+  //\r
+  // Get the size of our descriptors and the capsule size. GetCapsuleInfo()\r
+  // returns the number of descriptors that actually point to data, so add\r
+  // one for a terminator. Do that below.\r
+  //\r
+  GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize);\r
+  if ((CapsuleSize == 0) || (NumDescriptors == 0)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Initialize our local copy of private data. When we're done, we'll create a\r
+  // descriptor for it as well so that it can be put into free memory without\r
+  // trashing anything.\r
+  //\r
+  PrivateData.Signature     = EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE;\r
+  PrivateData.CapsuleSize   = (UINT32) CapsuleSize;\r
+  PrivateDataDesc[0].Union.DataBlock  = (EFI_PHYSICAL_ADDRESS) (UINTN) &PrivateData;\r
+  PrivateDataDesc[0].Length           = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA);\r
+  PrivateDataDesc[1].Union.DataBlock  = (EFI_PHYSICAL_ADDRESS) (UINTN) BlockList;\r
+  PrivateDataDesc[1].Length           = 0;\r
+  //\r
+  // In addition to PrivateDataDesc[1:0], one terminator is added\r
+  // See below RelocateBlockDescriptors()\r
+  //\r
+  NumDescriptors  += 3;\r
+  CapsuleSize     += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(CapsuleOffset) + sizeof(UINT32);\r
+  BlockList        = PrivateDataDesc;\r
+  DescriptorsSize  = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
+\r
+  //\r
+  // Don't go below some min address. If the base is below it,\r
+  // then move it up and adjust the size accordingly.\r
+  //\r
+  DEBUG ((EFI_D_INFO, "Capsule Memory range from 0x%8X to 0x%8X\n", (UINTN) *MemoryBase, (UINTN)*MemoryBase + *MemorySize));\r
+  if ((UINTN)*MemoryBase < (UINTN) MIN_COALESCE_ADDR) {\r
+    if (((UINTN)*MemoryBase + *MemorySize) < (UINTN) MIN_COALESCE_ADDR) {\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    } else {\r
+      *MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase);\r
+      *MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR;\r
+    }\r
+  }\r
+\r
+  if (*MemorySize <= (CapsuleSize + DescriptorsSize)) {\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  FreeMemBase = *MemoryBase;\r
+  FreeMemSize = *MemorySize;\r
+  DEBUG ((EFI_D_INFO, "Capsule Free Memory from 0x%8X to 0x%8X\n", (UINTN) FreeMemBase, (UINTN) FreeMemBase + FreeMemSize));\r
+\r
+  //\r
+  // Relocate all the block descriptors to low memory to make further\r
+  // processing easier.\r
+  //\r
+  BlockList = RelocateBlockDescriptors (PeiServices, BlockList, FreeMemBase, FreeMemSize);\r
+  if (BlockList == NULL) {\r
+    //\r
+    // Not enough room to relocate the descriptors\r
+    //\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  //\r
+  // Take the top of memory for the capsule. Naturally align.\r
+  //\r
+  DestPtr         = FreeMemBase + FreeMemSize - CapsuleSize;\r
+  DestPtr         = (UINT8 *) ((UINTN) DestPtr &~ (UINTN) (sizeof (UINTN) - 1));\r
+  FreeMemBase     = (UINT8 *) BlockList + DescriptorsSize;\r
+  FreeMemSize     = FreeMemSize - DescriptorsSize - CapsuleSize;\r
+  NewCapsuleBase  = (VOID *) DestPtr;\r
+\r
+  //\r
+  // Move all the blocks to the top (high) of memory.\r
+  // Relocate all the obstructing blocks. Note that the block descriptors\r
+  // were coalesced when they were relocated, so we can just ++ the pointer.\r
+  //\r
+  CurrentBlockDesc = BlockList;\r
+  while ((CurrentBlockDesc->Length != 0) || (CurrentBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
+    //\r
+    // See if any of the remaining capsule blocks are in the way\r
+    //\r
+    TempBlockDesc = CurrentBlockDesc;\r
+    while (TempBlockDesc->Length != 0) {\r
+      //\r
+      // Is this block in the way of where we want to copy the current descriptor to?\r
+      //\r
+      if (IsOverlapped (\r
+            (UINT8 *) DestPtr,\r
+            (UINTN) CurrentBlockDesc->Length,\r
+            (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,\r
+            (UINTN) TempBlockDesc->Length\r
+            )) {\r
+        //\r
+        // Relocate the block\r
+        //\r
+        RelocPtr = FindFreeMem (BlockList, FreeMemBase, FreeMemSize, (UINTN) TempBlockDesc->Length);\r
+        if (RelocPtr == NULL) {\r
+          return EFI_BUFFER_TOO_SMALL;\r
+        }\r
+\r
+        CopyMem ((VOID *) RelocPtr, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);\r
+        DEBUG ((EFI_D_INFO, "Capsule reloc data block from 0x%8X to 0x%8X with size 0x%8X\n",\r
+                (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) RelocPtr, (UINTN) TempBlockDesc->Length));\r
+\r
+        TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocPtr;\r
+      }\r
+      //\r
+      // Next descriptor\r
+      //\r
+      TempBlockDesc++;\r
+    }\r
+    //\r
+    // Ok, we made it through. Copy the block.\r
+    // we just support greping one capsule from the lists of block descs list.\r
+    //\r
+    CapsuleTimes ++;\r
+    //\r
+    //Skip the first block descriptor that filled with EFI_CAPSULE_PEIM_PRIVATE_DATA\r
+    //\r
+    if (CapsuleTimes > 1) {\r
+      //\r
+      //For every capsule entry point, check its header to determine whether to relocate it.\r
+      //If it is invalid, skip it and move on to the next capsule. If it is valid, relocate it.\r
+      //\r
+      if (CapsuleBeginFlag) {\r
+        CapsuleBeginFlag  = FALSE;\r
+        CapsuleHeader     = (EFI_CAPSULE_HEADER*)(UINTN)CurrentBlockDesc->Union.DataBlock;\r
+        SizeLeft          = CapsuleHeader->CapsuleImageSize;\r
+        if (!IsCapsuleCorrupted (CapsuleHeader)) {\r
+\r
+          if (CapsuleIndex > (MAX_SUPPORT_CAPSULE_NUM - 1)) {\r
+            DEBUG ((EFI_D_ERROR, "Capsule number exceeds the max number of %d!\n", MAX_SUPPORT_CAPSULE_NUM));\r
+            return  EFI_BUFFER_TOO_SMALL;\r
+          }\r
+\r
+          //\r
+          // Relocate this valid capsule\r
+          //\r
+          IsCorrupted  = FALSE;\r
+          CapsuleImageSize += SizeLeft;\r
+          CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);\r
+          DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8lX to 0x%8lX with size 0x%8X\n",CapsuleTimes,\r
+                 (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));\r
+          //\r
+          // Cache the begin offset of this capsule\r
+          //\r
+          CapsuleOffset[CapsuleIndex++] = (UINT32) (UINTN) DestPtr - (UINT32)(UINTN)NewCapsuleBase - (UINT32)sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA);\r
+          DestPtr += CurrentBlockDesc->Length;\r
+        }\r
+        //\r
+        // If the current block length is greater than or equal to SizeLeft, this is the \r
+        // start of the next capsule\r
+        //\r
+        if (CurrentBlockDesc->Length < SizeLeft) {\r
+          SizeLeft -= CurrentBlockDesc->Length;\r
+        } else {\r
+          //\r
+          // Start the next cycle\r
+          //\r
+          SizeLeft         = 0;\r
+          IsCorrupted      = TRUE;\r
+          CapsuleBeginFlag = TRUE;          \r
+        }\r
+      } else {\r
+        //\r
+        //Go on relocating the current capule image.\r
+        //\r
+        if (CurrentBlockDesc->Length < SizeLeft) {\r
+          if (!IsCorrupted) {\r
+            CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);\r
+            DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8lX to 0x%8lX with size 0x%8X\n",CapsuleTimes,\r
+                   (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));\r
+            DestPtr += CurrentBlockDesc->Length;\r
+          }\r
+          SizeLeft -= CurrentBlockDesc->Length;\r
+        } else {\r
+          //\r
+          //Here is the end of the current capsule image.\r
+          //\r
+          if (!IsCorrupted) {\r
+            CopyMem ((VOID *) DestPtr, (VOID *)(UINTN)(CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);\r
+            DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8lX to 0x%8lX with size 0x%8X\n",CapsuleTimes,\r
+                   (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));\r
+            DestPtr += CurrentBlockDesc->Length;\r
+          }\r
+          //\r
+          // Start the next cycle\r
+          //\r
+          SizeLeft = 0;\r
+          IsCorrupted = TRUE;\r
+          CapsuleBeginFlag = TRUE; \r
+        }\r
+      }\r
+    } else {\r
+      //\r
+      //The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.\r
+      //\r
+      CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);\r
+      DestPtr += CurrentBlockDesc->Length;\r
+    }\r
+    //\r
+    //Walk through the block descriptor list.\r
+    //\r
+    CurrentBlockDesc++;\r
+  }\r
+  //\r
+  // We return the base of memory we want reserved, and the size.\r
+  // The memory peim should handle it appropriately from there.\r
+  //\r
+  *MemorySize = (UINTN) CapsuleImageSize;\r
+  *MemoryBase = (VOID *) NewCapsuleBase;\r
+\r
+  //\r
+  //Append the offsets of mutiply capsules to the continous buffer\r
+  //\r
+  DataPtr    = (VOID*)((UINTN)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (UINTN)CapsuleImageSize);\r
+  AddDataPtr = (UINT32*)(((UINTN) DataPtr + sizeof(UINT32) - 1) &~ (UINT32) (sizeof (UINT32) - 1));\r
+\r
+  *AddDataPtr++ = CapsuleIndex;\r
+\r
+  CopyMem (AddDataPtr, &CapsuleOffset[0], sizeof (UINT32) * CapsuleIndex);\r
+\r
+  PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase;\r
+  PrivateDataPtr->CapsuleSize = (UINT32) CapsuleImageSize;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
diff --git a/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h b/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h
new file mode 100644 (file)
index 0000000..5c0aa30
--- /dev/null
@@ -0,0 +1,84 @@
+/** @file\r
+  Common header file.\r
+\r
+Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _CAPSULE_THUNK_32_TO_64_\r
+#define _CAPSULE_THUNK_32_TO_64_\r
+\r
+#include <Uefi.h>\r
+#include "PiPei.h"\r
+\r
+//\r
+// This capsule PEIM puts its private data at the start of the\r
+// coalesced capsule. Here's the structure definition.\r
+//\r
+#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'D')\r
+\r
+typedef struct {\r
+  UINT32  Signature;\r
+  UINTN   CapsuleSize;\r
+} EFI_CAPSULE_PEIM_PRIVATE_DATA;\r
+\r
+#define CAPSULE_TEST_SIGNATURE SIGNATURE_32('T', 'E', 'S', 'T')\r
+\r
+typedef struct {\r
+  EFI_PHYSICAL_ADDRESS  EntryPoint;\r
+  EFI_PHYSICAL_ADDRESS  StackBufferBase;\r
+  UINT64                StackBufferLength;\r
+  EFI_PHYSICAL_ADDRESS  JumpBuffer;\r
+  EFI_PHYSICAL_ADDRESS  BlockListAddr;\r
+  EFI_PHYSICAL_ADDRESS  MemoryBase64Ptr;\r
+  EFI_PHYSICAL_ADDRESS  MemorySize64Ptr;\r
+} SWITCH_32_TO_64_CONTEXT;\r
+\r
+typedef struct {\r
+  UINT16                ReturnCs;\r
+  EFI_PHYSICAL_ADDRESS  ReturnEntryPoint;\r
+  UINT64                ReturnStatus;\r
+  IA32_DESCRIPTOR       Gdtr;\r
+} SWITCH_64_TO_32_CONTEXT;\r
+\r
+/**\r
+  The function to coalesce a fragmented capsule in memory.\r
+\r
+  @param PeiServices        General purpose services available to every PEIM.\r
+  @param BlockListBuffer    Point to the buffer of Capsule Descriptor Variables.\r
+  @param MemoryBase         Pointer to the base of a block of memory that we can walk\r
+                            all over while trying to coalesce our buffers.\r
+                            On output, this variable will hold the base address of\r
+                            a coalesced capsule.\r
+  @param MemorySize         Size of the memory region pointed to by MemoryBase.\r
+                            On output, this variable will contain the size of the\r
+                            coalesced capsule.\r
+\r
+  @retval EFI_NOT_FOUND     if we can't determine the boot mode\r
+                            if the boot mode is not flash-update\r
+                            if we could not find the capsule descriptors\r
+\r
+  @retval EFI_BUFFER_TOO_SMALL\r
+                            if we could not coalesce the capsule in the memory\r
+                            region provided to us\r
+\r
+  @retval EFI_SUCCESS       if there's no capsule, or if we processed the\r
+                            capsule successfully.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CapsuleDataCoalesce (\r
+  IN EFI_PEI_SERVICES                **PeiServices,\r
+  IN IN EFI_PHYSICAL_ADDRESS         *BlockListBuffer,\r
+  IN OUT VOID                        **MemoryBase,\r
+  IN OUT UINTN                       *MemorySize\r
+  );\r
+\r
+#endif\r
index 7c10a3c56887a711c80bf149d1d1ba91a4a9bb83..5598755d4623c964c8ad874b29f3fb454d88dccf 100644 (file)
@@ -16,136 +16,420 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 \r
 #include "Capsule.h"\r
 \r
-EFI_PHYSICAL_ADDRESS           *mBufferAddress;\r
-\r
-/**\r
-  Check every capsule header.\r
-\r
-  @param CapsuleHeader   The pointer to EFI_CAPSULE_HEADER\r
+//\r
+// Global Descriptor Table (GDT)\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries[] = {\r
+/* selector { Global Segment Descriptor                              } */\r
+/* 0x00 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}}, //null descriptor\r
+/* 0x08 */  {{0xffff, 0,  0,  0x3,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //linear data segment descriptor\r
+/* 0x10 */  {{0xffff, 0,  0,  0xf,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //linear code segment descriptor\r
+/* 0x18 */  {{0xffff, 0,  0,  0x3,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //system data segment descriptor\r
+/* 0x20 */  {{0xffff, 0,  0,  0xb,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //system code segment descriptor\r
+/* 0x28 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}}, //spare segment descriptor\r
+/* 0x30 */  {{0xffff, 0,  0,  0x3,  1,  0,  1,  0xf,  0,  0, 1,  1,  0}}, //system data segment descriptor\r
+/* 0x38 */  {{0xffff, 0,  0,  0xb,  1,  0,  1,  0xf,  0,  1, 0,  1,  0}}, //system code segment descriptor\r
+/* 0x40 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}}, //spare segment descriptor\r
+};\r
 \r
-  @retval FALSE  Capsule is OK\r
-  @retval TRUE   Capsule is corrupted \r
+//\r
+// IA32 Gdt register\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {\r
+  sizeof (mGdtEntries) - 1,\r
+  (UINTN) mGdtEntries\r
+  };\r
 \r
+/**\r
+  Calculate the total size of page table.\r
+  \r
+  @return The size of page table.\r
+  \r
+  \r
 **/\r
-BOOLEAN\r
-IsCapsuleCorrupted (\r
-  IN EFI_CAPSULE_HEADER       *CapsuleHeader\r
+UINTN\r
+CalculatePageTableSize (\r
+  VOID\r
   )\r
 {\r
+  UINTN                                         TotalPagesNum;\r
+  UINT8                                         PhysicalAddressBits;\r
+  VOID                                          *Hob;\r
+  UINT32                                        NumberOfPml4EntriesNeeded;\r
+  UINT32                                        NumberOfPdpEntriesNeeded;\r
+\r
   //\r
-  //A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET.\r
+  // Get physical address bits supported from CPU HOB.\r
   //\r
-  if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {\r
-    return TRUE;\r
+  PhysicalAddressBits = 36;\r
+  \r
+  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
+  if (Hob != NULL) {\r
+    PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
   }\r
+\r
   //\r
-  //Make sure the flags combination is supported by the platform.\r
+  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
   //\r
-  if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {\r
-    return TRUE;\r
+  ASSERT (PhysicalAddressBits <= 52);\r
+  if (PhysicalAddressBits > 48) {\r
+    PhysicalAddressBits = 48;\r
   }\r
-  if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {\r
-    return TRUE;\r
+\r
+  //\r
+  // Calculate the table entries needed.\r
+  //\r
+  if (PhysicalAddressBits <= 39 ) {\r
+    NumberOfPml4EntriesNeeded = 1;\r
+    NumberOfPdpEntriesNeeded =  1 << (PhysicalAddressBits - 30);\r
+  } else {\r
+    NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);\r
+    NumberOfPdpEntriesNeeded = 512;\r
   }\r
 \r
-  return FALSE;\r
+  TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;\r
+\r
+  return EFI_PAGES_TO_SIZE (TotalPagesNum);\r
 }\r
 \r
 /**\r
-  Check the integrity of the capsule descriptors.\r
-\r
-  @param BlockList    Pointer to the capsule descriptors\r
+  Allocates and fills in the Page Directory and Page Table Entries to\r
+  establish a 1:1 Virtual to Physical mapping.\r
 \r
-  @retval NULL           BlockList is not valid.\r
-  @retval LastBlockDesc  Last one Block in BlockList\r
+  @param[in]  PageTablesAddress  The base address of page table.\r
 \r
 **/\r
-EFI_CAPSULE_BLOCK_DESCRIPTOR *\r
-ValidateCapsuleIntegrity (\r
-  IN EFI_CAPSULE_BLOCK_DESCRIPTOR    *BlockList\r
+VOID\r
+CreateIdentityMappingPageTables (\r
+  IN  EFI_PHYSICAL_ADDRESS  PageTablesAddress\r
   )\r
-{\r
-  EFI_CAPSULE_HEADER             *CapsuleHeader;\r
-  UINT64                         CapsuleSize;\r
-  UINT32                         CapsuleCount;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR   *Ptr;\r
-\r
-  //\r
-  // Go through the list to look for inconsistencies. Check for:\r
-  //   * misaligned block descriptors.\r
-  //   * The first capsule header guid\r
-  //   * The first capsule header flag\r
-  //   * Data + Length < Data (wrap)\r
-  CapsuleSize  = 0;\r
-  CapsuleCount = 0;\r
-  Ptr = BlockList;\r
-  while ((Ptr->Length != 0) || (Ptr->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
+{  \r
+  UINT8                                         PhysicalAddressBits;\r
+  EFI_PHYSICAL_ADDRESS                          PageAddress;\r
+  UINTN                                         IndexOfPml4Entries;\r
+  UINTN                                         IndexOfPdpEntries;\r
+  UINTN                                         IndexOfPageDirectoryEntries;\r
+  UINT32                                        NumberOfPml4EntriesNeeded;\r
+  UINT32                                        NumberOfPdpEntriesNeeded;\r
+  PAGE_MAP_AND_DIRECTORY_POINTER                *PageMapLevel4Entry;\r
+  PAGE_MAP_AND_DIRECTORY_POINTER                *PageMap;\r
+  PAGE_MAP_AND_DIRECTORY_POINTER                *PageDirectoryPointerEntry;\r
+  PAGE_TABLE_ENTRY                              *PageDirectoryEntry;\r
+  UINTN                                         BigPageAddress;\r
+  VOID                                          *Hob;\r
+\r
+  //\r
+  // Get physical address bits supported from CPU HOB.\r
+  //\r
+  PhysicalAddressBits = 36;\r
+  \r
+  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
+  if (Hob != NULL) {\r
+    PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
+  }\r
+\r
+  //\r
+  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
+  //\r
+  ASSERT (PhysicalAddressBits <= 52);\r
+  if (PhysicalAddressBits > 48) {\r
+    PhysicalAddressBits = 48;\r
+  }\r
+\r
+  //\r
+  // Calculate the table entries needed.\r
+  //\r
+  if (PhysicalAddressBits <= 39 ) {\r
+    NumberOfPml4EntriesNeeded = 1;\r
+    NumberOfPdpEntriesNeeded =  1 << (PhysicalAddressBits - 30);\r
+  } else {\r
+    NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);\r
+    NumberOfPdpEntriesNeeded = 512;\r
+  }\r
+\r
+  //\r
+  // Pre-allocate big pages to avoid later allocations. \r
+  //\r
+  BigPageAddress = (UINTN) PageTablesAddress;\r
+\r
+  //\r
+  // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.\r
+  //\r
+  PageMap         = (VOID *) BigPageAddress;\r
+  BigPageAddress += EFI_PAGE_SIZE;\r
+\r
+  PageMapLevel4Entry = PageMap;\r
+  PageAddress        = 0;\r
+  for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
     //\r
-    // Make sure the descriptor is aligned at UINT64 in memory\r
+    // Each PML4 entry points to a page of Page Directory Pointer entires.\r
+    // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.\r
     //\r
-    if ((UINTN) Ptr & 0x07) {\r
-      DEBUG ((EFI_D_ERROR, "BlockList address failed alignment check\n"));\r
-      return NULL;\r
-    }\r
+    PageDirectoryPointerEntry = (VOID *) BigPageAddress;\r
+    BigPageAddress += EFI_PAGE_SIZE;\r
 \r
-    if (Ptr->Length == 0) {\r
-      //\r
-      // Descriptor points to another list of block descriptors somewhere\r
-      // else.\r
+    //\r
+    // Make a PML4 Entry\r
+    //\r
+    PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;\r
+    PageMapLevel4Entry->Bits.ReadWrite = 1;\r
+    PageMapLevel4Entry->Bits.Present = 1;\r
+\r
+    for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {\r
       //\r
-      Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Ptr->Union.ContinuationPointer;\r
-    } else {\r
+      // Each Directory Pointer entries points to a page of Page Directory entires.\r
+      // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.\r
+      //       \r
+      PageDirectoryEntry = (VOID *) BigPageAddress;\r
+      BigPageAddress += EFI_PAGE_SIZE;\r
+\r
       //\r
-      //To enhance the reliability of check-up, the first capsule's header is checked here.\r
-      //More reliabilities check-up will do later.\r
+      // Fill in a Page Directory Pointer Entries\r
       //\r
-      if (CapsuleSize == 0) {\r
+      PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;\r
+      PageDirectoryPointerEntry->Bits.ReadWrite = 1;\r
+      PageDirectoryPointerEntry->Bits.Present = 1;\r
+\r
+      for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += 0x200000) {\r
         //\r
-        //Move to the first capsule to check its header.\r
+        // Fill in the Page Directory entries\r
         //\r
-        CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Ptr->Union.DataBlock);\r
-        if (IsCapsuleCorrupted (CapsuleHeader)) {\r
-          return NULL;\r
-        }\r
-        CapsuleCount ++;\r
-        CapsuleSize = CapsuleHeader->CapsuleImageSize;\r
-      } else {\r
-        if (CapsuleSize >= Ptr->Length) {\r
-          CapsuleSize = CapsuleSize - Ptr->Length;\r
-        } else {\r
-          CapsuleSize = 0;\r
-        }\r
+        PageDirectoryEntry->Uint64 = (UINT64)PageAddress;\r
+        PageDirectoryEntry->Bits.ReadWrite = 1;\r
+        PageDirectoryEntry->Bits.Present = 1;\r
+        PageDirectoryEntry->Bits.MustBe1 = 1;\r
+\r
       }\r
-      //\r
-      // Move to next BLOCK descriptor\r
-      //\r
-      Ptr++;\r
     }\r
   }\r
 \r
-  if (CapsuleCount == 0) {\r
+  //\r
+  // For the PML4 entries we are not using fill in a null entry.\r
+  // For now we just copy the first entry.\r
+  //\r
+  for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
+     CopyMem (\r
+       PageMapLevel4Entry,\r
+       PageMap,\r
+       sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)\r
+       );\r
+  }\r
+}\r
+\r
+/**\r
+  Return function from long mode to 32-bit mode.\r
+\r
+  @param  EntrypointContext  Context for mode switching\r
+  @param  ReturnContext      Context for mode switching\r
+\r
+**/\r
+VOID\r
+ReturnFunction (\r
+  SWITCH_32_TO_64_CONTEXT  *EntrypointContext,\r
+  SWITCH_64_TO_32_CONTEXT  *ReturnContext\r
+  )\r
+{ \r
+  //\r
+  // Restore original GDT\r
+  //\r
+  AsmWriteGdtr (&ReturnContext->Gdtr);\r
+  \r
+  //\r
+  // return to original caller\r
+  //\r
+  LongJump ((BASE_LIBRARY_JUMP_BUFFER  *)(UINTN)EntrypointContext->JumpBuffer, 1);\r
\r
+  //\r
+  // never be here\r
+  // \r
+  ASSERT (FALSE);\r
+}\r
+\r
+/**\r
+  Thunk function from 32-bit protection mode to long mode.\r
+\r
+  @param  PageTableAddress  Page table base address\r
+  @param  Context           Context for mode switching\r
+  @param  ReturnContext     Context for mode switching\r
+\r
+  @retval EFI_SUCCESS  Function successfully executed.\r
+\r
+**/\r
+EFI_STATUS\r
+Thunk32To64 (\r
+  EFI_PHYSICAL_ADDRESS          PageTableAddress,\r
+  SWITCH_32_TO_64_CONTEXT       *Context,\r
+  SWITCH_64_TO_32_CONTEXT       *ReturnContext\r
+  )\r
+{\r
+  UINTN                       SetJumpFlag;\r
+  EFI_STATUS                  Status;\r
+\r
+  //\r
+  // Save return address, LongJump will return here then\r
+  //\r
+  SetJumpFlag = SetJump ((BASE_LIBRARY_JUMP_BUFFER  *) (UINTN) Context->JumpBuffer);\r
+\r
+  if (SetJumpFlag == 0) {\r
+\r
     //\r
-    // No any capsule is found in BlockList.\r
+    // Build Page Tables for all physical memory processor supports\r
     //\r
-    return NULL;\r
-  }\r
+    CreateIdentityMappingPageTables (PageTableAddress);\r
+    \r
+    //\r
+    // Create 64-bit GDT\r
+    //\r
+    AsmWriteGdtr (&mGdt);\r
+\r
+    //\r
+    // Write CR3\r
+    //\r
+    AsmWriteCr3 ((UINTN) PageTableAddress);\r
 \r
-  return Ptr;\r
+    //\r
+    // Transfer to long mode\r
+    //\r
+    AsmEnablePaging64 (\r
+       0x38,\r
+      (UINT64) Context->EntryPoint,\r
+      (UINT64)(UINTN) Context,\r
+      (UINT64)(UINTN) ReturnContext,\r
+      Context->StackBufferBase + Context->StackBufferLength\r
+      );\r
+  }\r
+  \r
+  //\r
+  // Convert to 32-bit Status and return\r
+  //\r
+  Status = EFI_SUCCESS;\r
+  if ((UINTN) ReturnContext->ReturnStatus != 0) {\r
+    Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus);\r
+  }\r
+  \r
+  return Status;\r
 }\r
 \r
+/**\r
+  If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.\r
+\r
+  @param  LongModeBuffer            The context of long mode.\r
+  @param  CoalesceEntry             Entry of coalesce image.\r
+  @param  BlockListAddr             Address of block list.\r
+  @param  MemoryBase                Base of memory range.\r
+  @param  MemorySize                Size of memory range.\r
+\r
+  @retval EFI_SUCCESS               Successfully switched to long mode and execute coalesce.\r
+  @retval Others                    Failed to execute coalesce in long mode.\r
+\r
+**/\r
+EFI_STATUS\r
+ModeSwitch (\r
+  IN EFI_CAPSULE_LONG_MODE_BUFFER   *LongModeBuffer,\r
+  IN COALESCE_ENTRY                 CoalesceEntry,\r
+  IN EFI_PHYSICAL_ADDRESS           BlockListAddr,\r
+  IN OUT VOID                       **MemoryBase,\r
+  IN OUT UINTN                      *MemorySize\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_PHYSICAL_ADDRESS                 MemoryBase64;\r
+  UINT64                               MemorySize64;\r
+  EFI_PHYSICAL_ADDRESS                 MemoryEnd64;\r
+  SWITCH_32_TO_64_CONTEXT              Context;\r
+  SWITCH_64_TO_32_CONTEXT              ReturnContext;\r
+  BASE_LIBRARY_JUMP_BUFFER             JumpBuffer;\r
+  EFI_PHYSICAL_ADDRESS                 ReservedRangeBase;\r
+  EFI_PHYSICAL_ADDRESS                 ReservedRangeEnd;\r
+\r
+  ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT));\r
+  ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT));\r
+  \r
+  MemoryBase64  = (UINT64) (UINTN) *MemoryBase;\r
+  MemorySize64  = (UINT64) (UINTN) *MemorySize;\r
+  MemoryEnd64   = MemoryBase64 + MemorySize64;\r
+\r
+  //\r
+  // Merge memory range reserved for stack and page table  \r
+  //\r
+  if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {\r
+    ReservedRangeBase = LongModeBuffer->StackBaseAddress;\r
+    ReservedRangeEnd  = LongModeBuffer->PageTableAddress + CalculatePageTableSize ();\r
+  } else {\r
+    ReservedRangeBase = LongModeBuffer->PageTableAddress;\r
+    ReservedRangeEnd  = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize;\r
+  }\r
+  \r
+  //\r
+  // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.\r
+  // If they are overlapped, get a larger range to process capsule data.\r
+  //\r
+  if (ReservedRangeBase <= MemoryBase64) {\r
+    if (ReservedRangeEnd < MemoryEnd64) {\r
+      MemoryBase64 = ReservedRangeEnd;\r
+    } else {\r
+      DEBUG ((EFI_D_ERROR, "Memory is not enough to process capsule!\n"));\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  } else if (ReservedRangeBase < MemoryEnd64) {\r
+    if (ReservedRangeEnd < MemoryEnd64   &&\r
+        ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) {\r
+      MemoryBase64 = ReservedRangeEnd;\r
+    } else {\r
+      MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64);\r
+    }\r
+  }  \r
+  \r
+  //\r
+  // Initialize context jumping to 64-bit enviroment\r
+  //\r
+  Context.JumpBuffer            = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer;\r
+  Context.StackBufferBase       = LongModeBuffer->StackBaseAddress;\r
+  Context.StackBufferLength     = LongModeBuffer->StackSize;\r
+  Context.EntryPoint            = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry;\r
+  Context.BlockListAddr         = BlockListAddr;\r
+  Context.MemoryBase64Ptr       = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;\r
+  Context.MemorySize64Ptr       = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;\r
+\r
+  //\r
+  // Prepare data for return back\r
+  //\r
+  ReturnContext.ReturnCs           = 0x10;\r
+  ReturnContext.ReturnEntryPoint   = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction;\r
+  //\r
+  // Will save the return status of processing capsule\r
+  //\r
+  ReturnContext.ReturnStatus       = 0;\r
+  \r
+  //\r
+  // Save original GDT\r
+  //\r
+  AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr);\r
+  \r
+  Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext);\r
+  \r
+  if (!EFI_ERROR (Status)) {\r
+    *MemoryBase = (VOID *) (UINTN) MemoryBase64;\r
+    *MemorySize = (UINTN) MemorySize64;\r
+  }\r
+\r
+  return Status;\r
+\r
+}\r
 \r
 /**\r
   Checks for the presence of capsule descriptors.\r
   Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...\r
+  and save to DescriptorBuffer.\r
 \r
-  @param BlockList        Pointer to the capsule descriptors\r
+  @param DescriptorBuffer        Pointer to the capsule descriptors\r
 \r
   @retval EFI_SUCCESS     a valid capsule is present\r
   @retval EFI_NOT_FOUND   if a valid capsule is not present\r
 **/\r
 EFI_STATUS\r
 GetCapsuleDescriptors (\r
-  IN OUT EFI_CAPSULE_BLOCK_DESCRIPTOR    **BlockList OPTIONAL\r
+  IN EFI_PHYSICAL_ADDRESS     *DescriptorBuffer\r
   )\r
 {\r
   EFI_STATUS                       Status;\r
@@ -157,14 +441,8 @@ GetCapsuleDescriptors (
   CHAR16                           CapsuleVarName[30];\r
   CHAR16                           *TempVarName;\r
   EFI_PHYSICAL_ADDRESS             CapsuleDataPtr64;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR     *LastBlock;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR     *TempBlock;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR     *HeadBlock;\r
   EFI_PEI_READ_ONLY_VARIABLE2_PPI  *PPIVariableServices;\r
 \r
-  LastBlock         = NULL;\r
-  HeadBlock         = NULL;\r
-  TempBlock         = NULL;\r
   Index             = 0;\r
   TempVarName       = NULL;\r
   CapsuleVarName[0] = 0;\r
@@ -204,20 +482,9 @@ GetCapsuleDescriptors (
         // pointer is null. In that case, return success since we know that the\r
         // variable is set.\r
         //\r
-        if (BlockList == NULL) {\r
+        if (DescriptorBuffer == NULL) {\r
           return EFI_SUCCESS;\r
         }\r
-        //\r
-        // Test integrity of descriptors.\r
-        //\r
-        LastBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64);\r
-        if (LastBlock == NULL) {\r
-          return EFI_NOT_FOUND;\r
-        }\r
-        //\r
-        // Return the base of the block descriptors\r
-        //\r
-        HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64;\r
       } else {\r
         UnicodeValueToString (TempVarName, 0, Index, 0);\r
         Status = PPIVariableServices->GetVariable (\r
@@ -237,7 +504,7 @@ GetCapsuleDescriptors (
         //\r
         Flag = FALSE;\r
         for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {\r
-          if (mBufferAddress[TempIndex] == CapsuleDataPtr64) {\r
+          if (DescriptorBuffer[TempIndex] == CapsuleDataPtr64)  {\r
             Flag = TRUE;\r
             break;\r
           }\r
@@ -246,500 +513,122 @@ GetCapsuleDescriptors (
           Index ++;\r
           continue;\r
         }\r
-\r
-        //\r
-        // Test integrity of descriptors.\r
-        //\r
-        TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64);\r
-        if (TempBlock == NULL) {\r
-          return EFI_NOT_FOUND;\r
-        }\r
-        //\r
-        // Combine the different BlockList into single BlockList.\r
-        //\r
-        LastBlock->Union.DataBlock = CapsuleDataPtr64;\r
-        LastBlock->Length          = 0;\r
-        LastBlock                  = TempBlock;\r
       }\r
       \r
       //\r
       // Cache BlockList which has been processed\r
       //\r
-      mBufferAddress[ValidIndex++] = CapsuleDataPtr64;\r
+      DescriptorBuffer[ValidIndex++] = CapsuleDataPtr64;\r
       Index ++;\r
     }\r
   }\r
   \r
-  if (HeadBlock != NULL) {\r
-    *BlockList = HeadBlock;\r
-    return EFI_SUCCESS;\r
-  }\r
-  return EFI_NOT_FOUND;\r
-}\r
-\r
-/**\r
-  Given a pointer to a capsule block descriptor, traverse the list to figure\r
-  out how many legitimate descriptors there are, and how big the capsule it\r
-  refers to is.\r
-\r
-  @param Desc            Pointer to the capsule block descriptors\r
-                         NumDescriptors  - optional pointer to where to return the number of descriptors\r
-                         CapsuleSize     - optional pointer to where to return the capsule size\r
-  @param NumDescriptors  Optional pointer to where to return the number of descriptors\r
-  @param CapsuleSize     Optional pointer to where to return the capsule size\r
-\r
-  @retval EFI_NOT_FOUND   No descriptors containing data in the list\r
-  @retval EFI_SUCCESS     Return data is valid\r
-**/\r
-EFI_STATUS\r
-GetCapsuleInfo (\r
-  IN EFI_CAPSULE_BLOCK_DESCRIPTOR   *Desc,\r
-  IN OUT UINTN                      *NumDescriptors OPTIONAL,\r
-  IN OUT UINTN                      *CapsuleSize OPTIONAL\r
-  )\r
-{\r
-  UINTN Count;\r
-  UINTN Size;\r
-\r
-  ASSERT (Desc != NULL);\r
-\r
-  Count = 0;\r
-  Size  = 0;\r
-\r
-  while (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {\r
-    if (Desc->Length == 0) {\r
-      //\r
-      // Descriptor points to another list of block descriptors somewhere\r
-      //\r
-      Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Desc->Union.ContinuationPointer;\r
-    } else {\r
-      Size += (UINTN) Desc->Length;\r
-      Count++;\r
-      Desc++;\r
-    }\r
-  }\r
-  //\r
-  // If no descriptors, then fail\r
-  //\r
-  if (Count == 0) {\r
-    return EFI_NOT_FOUND;\r
-  }\r
-\r
-  if (NumDescriptors != NULL) {\r
-    *NumDescriptors = Count;\r
-  }\r
-\r
-  if (CapsuleSize != NULL) {\r
-    *CapsuleSize = Size;\r
-  }\r
-\r
   return EFI_SUCCESS;\r
 }\r
 \r
-\r
-/**\r
-  Try to verify the integrity of a capsule test pattern before the\r
-  capsule gets coalesced. This can be useful in narrowing down\r
-  where capsule data corruption occurs.\r
-\r
-  The test pattern mode fills in memory with a counting UINT32 value. \r
-  If the capsule is not divided up in a multiple of 4-byte blocks, then\r
-  things get messy doing the check. Therefore there are some cases\r
-  here where we just give up and skip the pre-coalesce check.\r
-\r
-  @param PeiServices  PEI services table\r
-  @param Desc         Pointer to capsule descriptors\r
-**/\r
-VOID\r
-CapsuleTestPatternPreCoalesce (\r
-  IN EFI_PEI_SERVICES              **PeiServices,\r
-  IN EFI_CAPSULE_BLOCK_DESCRIPTOR  *Desc\r
-  )\r
-{\r
-  UINT32  *TestPtr;\r
-  UINT32  TestCounter;\r
-  UINT32  TestSize;\r
-  //\r
-  // Find first data descriptor\r
-  //\r
-  while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
-    Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Desc->Union.ContinuationPointer;\r
-  }\r
-\r
-  if (Desc->Union.ContinuationPointer == 0) {\r
-    return ;\r
-  }\r
-  //\r
-  // First one better be long enough to at least hold the test signature\r
-  //\r
-  if (Desc->Length < sizeof (UINT32)) {\r
-    DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #1\n"));\r
-    return ;\r
-  }\r
-\r
-  TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;\r
-  if (*TestPtr != CAPSULE_TEST_SIGNATURE) {\r
-    return ;\r
-  }\r
-\r
-  TestCounter = 0;\r
-  TestSize    = (UINT32) Desc->Length - 2 * sizeof (UINT32);\r
-  //\r
-  // Skip over the signature and the size fields in the pattern data header\r
-  //\r
-  TestPtr += 2;\r
-  while (1) {\r
-    if ((TestSize & 0x03) != 0) {\r
-      DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #2\n"));\r
-      return ;\r
-    }\r
-\r
-    while (TestSize > 0) {\r
-      if (*TestPtr != TestCounter) {\r
-        DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce failed data corruption check\n"));\r
-        return ;\r
-      }\r
-\r
-      TestSize -= sizeof (UINT32);\r
-      TestCounter++;\r
-      TestPtr++;\r
-    }\r
-    Desc++;\r
-    while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
-      Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) Desc->Union.ContinuationPointer;\r
-    }\r
-\r
-    if (Desc->Union.ContinuationPointer == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {\r
-      return ;\r
-    }\r
-    TestSize = (UINT32) Desc->Length;\r
-    TestPtr  = (UINT32 *) (UINTN) Desc->Union.DataBlock;\r
-  }\r
-}\r
-\r
-\r
 /**\r
-  Determine if two buffers overlap in memory.\r
-\r
-  @param Buff1   pointer to first buffer\r
-  @param Size1   size of Buff1\r
-  @param Buff2   pointer to second buffer\r
-  @param Size2   size of Buff2\r
+  Locates the coalesce image entry point, and detects its machine type.\r
 \r
-  @retval TRUE    Buffers overlap in memory.\r
-  @retval FALSE   Buffer doesn't overlap.\r
-**/\r
-BOOLEAN\r
-IsOverlapped (\r
-  UINT8     *Buff1,\r
-  UINTN     Size1,\r
-  UINT8     *Buff2,\r
-  UINTN     Size2\r
-  )\r
-{\r
-  //\r
-  // If buff1's end is less than the start of buff2, then it's ok.\r
-  // Also, if buff1's start is beyond buff2's end, then it's ok.\r
-  //\r
-  if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) {\r
-    return FALSE;\r
-  }\r
+  @param CoalesceImageEntryPoint   Pointer to coalesce image entry point for output.\r
+  @param CoalesceImageMachineType  Pointer to machine type of coalesce image.\r
 \r
-  return TRUE;\r
-}\r
+  @retval EFI_SUCCESS     Coalesce image successfully located.\r
+  @retval Others          Failed to locate the coalesce image.\r
 \r
-/**\r
-  Given a pointer to the capsule block list, info on the available system\r
-  memory, and the size of a buffer, find a free block of memory where a\r
-  buffer of the given size can be copied to safely.\r
-\r
-  @param BlockList   Pointer to head of capsule block descriptors\r
-  @param MemBase     Pointer to the base of memory in which we want to find free space\r
-  @param MemSize     The size of the block of memory pointed to by MemBase\r
-  @param DataSize    How big a free block we want to find\r
-\r
-  @return A pointer to a memory block of at least DataSize that lies somewhere \r
-  between MemBase and (MemBase + MemSize). The memory pointed to does not\r
-  contain any of the capsule block descriptors or capsule blocks pointed to\r
-  by the BlockList.\r
 **/\r
-UINT8 *\r
-FindFreeMem (\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR      *BlockList,\r
-  UINT8                             *MemBase,\r
-  UINTN                             MemSize,\r
-  UINTN                             DataSize\r
+EFI_STATUS\r
+FindCapsuleCoalesceImage (\r
+  OUT EFI_PHYSICAL_ADDRESS    *CoalesceImageEntryPoint,\r
+  OUT UINT16                  *CoalesceImageMachineType\r
   )\r
 {\r
-  UINTN                           Size;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR    *CurrDesc;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR    *TempDesc;\r
-  UINT8                           *MemEnd;\r
-  BOOLEAN                         Failed;\r
-\r
-  //\r
-  // Need at least enough to copy the data to at the end of the buffer, so\r
-  // say the end is less the data size for easy comparisons here.\r
-  //\r
-  MemEnd    = MemBase + MemSize - DataSize;\r
-  CurrDesc  = BlockList;\r
-  //\r
-  // Go through all the descriptor blocks and see if any obstruct the range\r
-  //\r
-  while (CurrDesc != NULL) {\r
-    //\r
-    // Get the size of this block list and see if it's in the way\r
-    //\r
-    Failed    = FALSE;\r
-    TempDesc  = CurrDesc;\r
-    Size      = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
-    while (TempDesc->Length != 0) {\r
-      Size += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
-      TempDesc++;\r
-    }\r
+  EFI_STATUS                           Status;\r
+  UINTN                                Instance;\r
+  EFI_PEI_LOAD_FILE_PPI                *LoadFile;\r
+  EFI_PEI_FV_HANDLE                    VolumeHandle;\r
+  EFI_PEI_FILE_HANDLE                  FileHandle;\r
+  EFI_PHYSICAL_ADDRESS                 CoalesceImageAddress;\r
+  UINT64                               CoalesceImageSize;\r
+  UINT32                               AuthenticationState;\r
 \r
-    if (IsOverlapped (MemBase, DataSize, (UINT8 *) CurrDesc, Size)) {\r
-      //\r
-      // Set our new base to the end of this block list and start all over\r
-      //\r
-      MemBase   = (UINT8 *) CurrDesc + Size;\r
-      CurrDesc  = BlockList;\r
-      if (MemBase > MemEnd) {\r
-        return NULL;\r
-      }\r
+  Instance = 0;\r
 \r
-      Failed = TRUE;\r
+  while (TRUE) {\r
+    Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
     }\r
-    //\r
-    // Now go through all the blocks and make sure none are in the way\r
-    //\r
-    while ((CurrDesc->Length != 0) && (!Failed)) {\r
-      if (IsOverlapped (MemBase, DataSize, (UINT8 *) (UINTN) CurrDesc->Union.DataBlock, (UINTN) CurrDesc->Length)) {\r
-        //\r
-        // Set our new base to the end of this block and start all over\r
-        //\r
-        Failed    = TRUE;\r
-        MemBase   = (UINT8 *) ((UINTN) CurrDesc->Union.DataBlock) + CurrDesc->Length;\r
-        CurrDesc  = BlockList;\r
-        if (MemBase > MemEnd) {\r
-          return NULL;\r
-        }\r
+    Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile);\r
+      ASSERT_EFI_ERROR (Status);\r
+\r
+      Status = LoadFile->LoadFile (\r
+                           LoadFile,\r
+                           FileHandle,\r
+                           &CoalesceImageAddress,\r
+                           &CoalesceImageSize,\r
+                           CoalesceImageEntryPoint,\r
+                           &AuthenticationState\r
+                           );\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleRelocate image ffs %r!\n", Status));\r
+        return Status;\r
       }\r
-      CurrDesc++;\r
-    }\r
-    //\r
-    // Normal continuation -- jump to next block descriptor list\r
-    //\r
-    if (!Failed) {\r
-      CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) CurrDesc->Union.ContinuationPointer;\r
+      *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);\r
+      break;\r
+    } else {\r
+      continue;\r
     }\r
   }\r
-  return MemBase;\r
+\r
+  return Status;\r
 }\r
 \r
 /**\r
-  The capsule block descriptors may be fragmented and spread all over memory.\r
-  To simplify the coalescing of capsule blocks, first coalesce all the\r
-  capsule block descriptors low in memory.\r
+  Gets the reserved long mode buffer.\r
 \r
-  The descriptors passed in can be fragmented throughout memory. Here\r
-  they are relocated into memory to turn them into a contiguous (null\r
-  terminated) array.\r
+  @param  LongModeBuffer  Pointer to the long mode buffer for output.\r
 \r
-  @param PeiServices pointer to PEI services table\r
-  @param BlockList   pointer to the capsule block descriptors\r
-  @param MemBase     base of system memory in which we can work\r
-  @param MemSize     size of the system memory pointed to by MemBase\r
+  @retval EFI_SUCCESS     Long mode buffer successfully retrieved.\r
+  @retval Others          Variable storing long mode buffer not found.\r
 \r
-  @retval NULL    could not relocate the descriptors\r
-  @retval Pointer to the base of the successfully-relocated block descriptors. \r
 **/\r
-EFI_CAPSULE_BLOCK_DESCRIPTOR  *\r
-RelocateBlockDescriptors (\r
-  IN EFI_PEI_SERVICES                   **PeiServices,\r
-  IN EFI_CAPSULE_BLOCK_DESCRIPTOR       *BlockList,\r
-  IN UINT8                              *MemBase,\r
-  IN UINTN                              MemSize\r
+EFI_STATUS\r
+GetLongModeContext (\r
+  OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer\r
   )\r
 {\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR   *NewBlockList;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR   *CurrBlockDescHead;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR   *TempBlockDesc;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR   *PrevBlockDescTail;\r
-  UINTN                          NumDescriptors;\r
-  UINTN                          BufferSize;\r
-  UINT8                          *RelocBuffer;\r
-  UINTN                          BlockListSize;\r
-  //\r
-  // Get the info on the blocks and descriptors. Since we're going to move\r
-  // the descriptors low in memory, adjust the base/size values accordingly here.\r
-  // GetCapsuleInfo() returns the number of legit descriptors, so add one for\r
-  // a terminator.\r
-  //\r
-  if (GetCapsuleInfo (BlockList, &NumDescriptors, NULL) != EFI_SUCCESS) {\r
-    return NULL;\r
-  }\r
-\r
-  NumDescriptors++;\r
-  BufferSize    = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
-  NewBlockList  = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) MemBase;\r
-  if (MemSize < BufferSize) {\r
-    return NULL;\r
-  }\r
-\r
-  MemSize -= BufferSize;\r
-  MemBase += BufferSize;\r
-  //\r
-  // Go through all the blocks and make sure none are in the way\r
-  //\r
-  TempBlockDesc = BlockList;\r
-  while (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {\r
-    if (TempBlockDesc->Length == 0) {\r
-      //\r
-      // Next block of descriptors\r
-      //\r
-      TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) TempBlockDesc->Union.ContinuationPointer;\r
-    } else {\r
-      //\r
-      // If the capsule data pointed to by this descriptor is in the way,\r
-      // move it.\r
-      //\r
-      if (IsOverlapped (\r
-            (UINT8 *) NewBlockList,\r
-            BufferSize,\r
-            (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,\r
-            (UINTN) TempBlockDesc->Length\r
-            )) {\r
-        //\r
-        // Relocate the block\r
-        //\r
-        RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, (UINTN) TempBlockDesc->Length);\r
-        if (RelocBuffer == NULL) {\r
-          return NULL;\r
-        }\r
-\r
-        CopyMem ((VOID *) RelocBuffer, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);\r
-        TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;\r
-\r
-        DEBUG ((EFI_D_INFO, "Capsule relocate descriptors from/to/size  0x%X 0x%X 0x%X\n", (UINT32)(UINTN)TempBlockDesc->Union.DataBlock, (UINT32)(UINTN)RelocBuffer, (UINT32)(UINTN)TempBlockDesc->Length));\r
-      }\r
-    }\r
-    TempBlockDesc++;\r
-  }\r
-  //\r
-  // Now go through all the block descriptors to make sure that they're not\r
-  // in the memory region we want to copy them to.\r
-  //\r
-  CurrBlockDescHead = BlockList;\r
-  PrevBlockDescTail = NULL;\r
-  while ((CurrBlockDescHead != NULL) && (CurrBlockDescHead->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
-    //\r
-    // Get the size of this list then see if it overlaps our low region\r
-    //\r
-    TempBlockDesc = CurrBlockDescHead;\r
-    BlockListSize = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
-    while (TempBlockDesc->Length != 0) {\r
-      BlockListSize += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
-      TempBlockDesc++;\r
-    }\r
+  EFI_STATUS   Status;\r
+  UINTN        Size;\r
+  EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;\r
 \r
-    if (IsOverlapped (\r
-          (UINT8 *) NewBlockList,\r
-          BufferSize,\r
-          (UINT8 *) CurrBlockDescHead,\r
-          BlockListSize\r
-          )) {\r
-      //\r
-      // Overlaps, so move it out of the way\r
-      //\r
-      RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, BlockListSize);\r
-      if (RelocBuffer == NULL) {\r
-        return NULL;\r
-      }\r
-      CopyMem ((VOID *) RelocBuffer, (VOID *) CurrBlockDescHead, BlockListSize);\r
-      DEBUG ((EFI_D_INFO, "Capsule reloc descriptor block #2\n"));\r
-      //\r
-      // Point the previous block's next point to this copied version. If\r
-      // the tail pointer is null, then this is the first descriptor block.\r
-      //\r
-      if (PrevBlockDescTail == NULL) {\r
-        BlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) RelocBuffer;\r
-      } else {\r
-        PrevBlockDescTail->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;\r
-      }\r
-    }\r
-    //\r
-    // Save our new tail and jump to the next block list\r
-    //\r
-    PrevBlockDescTail = TempBlockDesc;\r
-    CurrBlockDescHead = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) TempBlockDesc->Union.ContinuationPointer;\r
-  }\r
-  //\r
-  // Cleared out low memory. Now copy the descriptors down there.\r
-  //\r
-  TempBlockDesc     = BlockList;\r
-  CurrBlockDescHead = NewBlockList;\r
-  while ((TempBlockDesc != NULL) && (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
-    if (TempBlockDesc->Length != 0) {\r
-      CurrBlockDescHead->Union.DataBlock = TempBlockDesc->Union.DataBlock;\r
-      CurrBlockDescHead->Length = TempBlockDesc->Length;\r
-      CurrBlockDescHead++;\r
-      TempBlockDesc++;\r
-    } else {\r
-      TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *) (UINTN) TempBlockDesc->Union.ContinuationPointer;\r
-    }\r
+  Status = PeiServicesLocatePpi (\r
+             &gEfiPeiReadOnlyVariable2PpiGuid,\r
+             0,\r
+             NULL,\r
+             (VOID **) &PPIVariableServices\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);\r
+  Status = PPIVariableServices->GetVariable (\r
+                                  PPIVariableServices,\r
+                                  EFI_CAPSULE_LONG_MODE_BUFFER_NAME,\r
+                                  &gEfiCapsuleVendorGuid,\r
+                                  NULL,\r
+                                  &Size,\r
+                                  LongModeBuffer\r
+                                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));\r
   }\r
-  //\r
-  // Null terminate\r
-  //\r
-  CurrBlockDescHead->Union.ContinuationPointer   = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL;\r
-  CurrBlockDescHead->Length = 0;\r
-  return NewBlockList;\r
+  return Status;\r
 }\r
 \r
 /**\r
   Capsule PPI service to coalesce a fragmented capsule in memory.\r
 \r
-  Memory Map for coalesced capsule:\r
-  MemBase +   ---->+---------------------------+<-----------+\r
-  MemSize          |    CapsuleOffset[49]      |            |\r
-                   +---------------------------+            |\r
-                   |    ................       |            |\r
-                   +---------------------------+            |\r
-                   |    CapsuleOffset[2]       |            |\r
-                   +---------------------------+            |\r
-                   |    CapsuleOffset[1]       |            |\r
-                   +---------------------------+            |\r
-                   |    CapsuleOffset[0]       |       CapsuleSize     \r
-                   +---------------------------+            |\r
-                   |    CapsuleNumber          |            |\r
-                   +---------------------------+            |\r
-                   |                           |            |       \r
-                   |                           |            |       \r
-                   |    Capsule Image          |            |   \r
-                   |                           |            |       \r
-                   |                           |            |       \r
-                   +---------------------------+            |\r
-                   |    PrivateData            |            |\r
-   DestPtr  ---->  +---------------------------+<-----------+\r
-                   |                           |            |\r
-                   |     FreeMem               |        FreeMemSize\r
-                   |                           |            |\r
-   FreeMemBase --->+---------------------------+<-----------+\r
-                   |    Terminator             |\r
-                   +---------------------------+\r
-                   |    BlockDescriptor n      |\r
-                   +---------------------------+\r
-                   |    .................      |\r
-                   +---------------------------+\r
-                   |    BlockDescriptor 1      |\r
-                   +---------------------------+\r
-                   |    BlockDescriptor 0      |\r
-                   +---------------------------+\r
-                   |    PrivateDataDesc 0      |\r
-      MemBase ---->+---------------------------+<----- BlockList\r
-\r
-\r
   @param PeiServices  General purpose services available to every PEIM.\r
   @param MemoryBase   Pointer to the base of a block of memory that we can walk\r
                       all over while trying to coalesce our buffers.\r
@@ -768,54 +657,26 @@ CapsuleCoalesce (
   IN OUT UINTN                       *MemorySize\r
   )\r
 {\r
-  VOID                           *NewCapsuleBase;\r
-  VOID                           *DataPtr;\r
-  UINT8                          CapsuleIndex;\r
-  UINT8                          *FreeMemBase;\r
-  UINT8                          *DestPtr;\r
-  UINT8                          *RelocPtr;\r
-  UINT32                         CapsuleOffset[MAX_SUPPORT_CAPSULE_NUM]; \r
-  UINT32                         *AddDataPtr;\r
-  UINT32                         CapsuleTimes; \r
-  UINT64                         SizeLeft; \r
-  UINT64                         CapsuleImageSize; \r
-  UINTN                          CapsuleSize;\r
-  UINTN                          DescriptorsSize;\r
-  UINTN                          FreeMemSize;\r
-  UINTN                          NumDescriptors;\r
-  UINTN                          Index;\r
-  UINTN                          Size;\r
-  UINTN                          VariableCount;\r
-  CHAR16                         CapsuleVarName[30];\r
-  CHAR16                         *TempVarName;\r
-  EFI_PHYSICAL_ADDRESS           CapsuleDataPtr64;  \r
-  BOOLEAN                        IsCorrupted;\r
-  BOOLEAN                        CapsuleBeginFlag;\r
-  EFI_STATUS                     Status;\r
-  EFI_BOOT_MODE                  BootMode;\r
-  EFI_CAPSULE_HEADER             *CapsuleHeader;\r
-  EFI_CAPSULE_PEIM_PRIVATE_DATA  PrivateData;\r
-  EFI_CAPSULE_PEIM_PRIVATE_DATA  *PrivateDataPtr;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR  *BlockList;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR  *CurrentBlockDesc;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR  *TempBlockDesc;\r
-  EFI_CAPSULE_BLOCK_DESCRIPTOR  PrivateDataDesc[2];\r
-  EFI_PEI_READ_ONLY_VARIABLE2_PPI  *PPIVariableServices;\r
-  \r
-  CapsuleIndex     = 0;\r
-  SizeLeft         = 0;\r
-  CapsuleTimes     = 0;\r
-  CapsuleImageSize = 0;\r
-  PrivateDataPtr   = NULL;\r
-  AddDataPtr       = NULL;\r
-  CapsuleHeader    = NULL;\r
-  CapsuleBeginFlag = TRUE;\r
-  IsCorrupted      = TRUE;\r
-  CapsuleSize      = 0;\r
-  NumDescriptors   = 0;\r
-  Index            = 0;\r
-  VariableCount    = 0;\r
-  CapsuleVarName[0] = 0;  \r
+  UINTN                                Index;\r
+  UINTN                                Size;\r
+  UINTN                                VariableCount;\r
+  CHAR16                               CapsuleVarName[30];\r
+  CHAR16                               *TempVarName;\r
+  EFI_PHYSICAL_ADDRESS                 CapsuleDataPtr64;  \r
+  EFI_STATUS                           Status;\r
+  EFI_BOOT_MODE                        BootMode;\r
+  EFI_PEI_READ_ONLY_VARIABLE2_PPI      *PPIVariableServices;\r
+  EFI_PHYSICAL_ADDRESS                 *VariableArrayAddress;\r
+#ifdef MDE_CPU_IA32\r
+  UINT16                               CoalesceImageMachineType;\r
+  EFI_PHYSICAL_ADDRESS                 CoalesceImageEntryPoint;\r
+  COALESCE_ENTRY                       CoalesceEntry;\r
+  EFI_CAPSULE_LONG_MODE_BUFFER         LongModeBuffer;\r
+#endif\r
+\r
+  Index                   = 0;\r
+  VariableCount           = 0;\r
+  CapsuleVarName[0]       = 0;\r
 \r
   //\r
   // Someone should have already ascertained the boot mode. If it's not\r
@@ -823,7 +684,9 @@ CapsuleCoalesce (
   //\r
   Status = PeiServicesGetBootMode (&BootMode);\r
   if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) {\r
-    return EFI_NOT_FOUND;\r
+    DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update path.\n"));    \r
+    Status = EFI_NOT_FOUND;\r
+    goto Done;\r
   }\r
   \r
   //\r
@@ -837,7 +700,7 @@ CapsuleCoalesce (
               (VOID **) &PPIVariableServices\r
               );\r
   if (EFI_ERROR (Status)) {\r
-    return Status;\r
+    goto Done;\r
   }\r
   Size = sizeof (CapsuleDataPtr64);\r
   StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME);\r
@@ -858,264 +721,94 @@ CapsuleCoalesce (
       //\r
       // There is no capsule variables, quit\r
       //\r
-      DEBUG ((EFI_D_ERROR,"Capsule variable Index = %d\n", Index));\r
+      DEBUG ((EFI_D_INFO,"Capsule variable Index = %d\n", Index));\r
       break;\r
     }\r
     VariableCount++;\r
     Index++;\r
   }\r
   \r
-  DEBUG ((EFI_D_ERROR,"Capsule variable count = %d\n", VariableCount));\r
+  DEBUG ((EFI_D_INFO,"Capsule variable count = %d\n", VariableCount));\r
   \r
+  //\r
+  // The last entry is the end flag.\r
+  //\r
   Status = PeiServicesAllocatePool (\r
-             VariableCount * sizeof (EFI_PHYSICAL_ADDRESS),\r
-             (VOID **)&mBufferAddress\r
+             (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS),\r
+             (VOID **)&VariableArrayAddress\r
              );\r
 \r
   if (Status != EFI_SUCCESS) {\r
     DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status));\r
-    return Status;\r
+    goto Done;\r
   }\r
   \r
+  ZeroMem (VariableArrayAddress, (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS));\r
+  \r
   //\r
   // Find out if we actually have a capsule.\r
+  // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.\r
   //\r
-  Status = GetCapsuleDescriptors (&BlockList);\r
+  Status = GetCapsuleDescriptors (VariableArrayAddress);\r
   if (EFI_ERROR (Status)) {\r
-    return Status;\r
-  }\r
-\r
-  DEBUG_CODE (\r
-    CapsuleTestPatternPreCoalesce (PeiServices, BlockList);\r
-  );\r
-\r
-  //\r
-  // Get the size of our descriptors and the capsule size. GetCapsuleInfo()\r
-  // returns the number of descriptors that actually point to data, so add\r
-  // one for a terminator. Do that below.\r
-  //\r
-  GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize);\r
-  if ((CapsuleSize == 0) || (NumDescriptors == 0)) {\r
-    return EFI_NOT_FOUND;\r
-  }\r
-\r
-  //\r
-  // Initialize our local copy of private data. When we're done, we'll create a\r
-  // descriptor for it as well so that it can be put into free memory without\r
-  // trashing anything.\r
-  //\r
-  PrivateData.Signature     = EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE;\r
-  PrivateData.CapsuleSize   = CapsuleSize;\r
-  PrivateDataDesc[0].Union.DataBlock  = (EFI_PHYSICAL_ADDRESS) (UINTN) &PrivateData;\r
-  PrivateDataDesc[0].Length           = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA);\r
-  PrivateDataDesc[1].Union.DataBlock  = (EFI_PHYSICAL_ADDRESS) (UINTN) BlockList;\r
-  PrivateDataDesc[1].Length           = 0;\r
-  //\r
-  // In addition to PrivateDataDesc[1:0], one terminator is added\r
-  // See below RelocateBlockDescriptors()\r
-  //\r
-  NumDescriptors  += 3;\r
-  CapsuleSize     += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(CapsuleOffset) + sizeof(UINT32);\r
-  BlockList        = PrivateDataDesc;\r
-  DescriptorsSize  = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
-\r
-  //\r
-  // Don't go below some min address. If the base is below it,\r
-  // then move it up and adjust the size accordingly.\r
-  //\r
-  DEBUG ((EFI_D_INFO, "Capsule Memory range from 0x%8X to 0x%8X\n", (UINTN) *MemoryBase, (UINTN)*MemoryBase + *MemorySize));\r
-  if ((UINTN)*MemoryBase < (UINTN) MIN_COALESCE_ADDR) {\r
-    if (((UINTN)*MemoryBase + *MemorySize) < (UINTN) MIN_COALESCE_ADDR) {\r
-      return EFI_BUFFER_TOO_SMALL;\r
-    } else {\r
-      *MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase);\r
-      *MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR;\r
-    }\r
-  }\r
-\r
-  if (*MemorySize <= (CapsuleSize + DescriptorsSize)) {\r
-    return EFI_BUFFER_TOO_SMALL;\r
+    DEBUG ((EFI_D_ERROR, "Fail to find capsule variables.\n"));\r
+    goto Done;\r
   }\r
 \r
-  FreeMemBase = *MemoryBase;\r
-  FreeMemSize = *MemorySize;\r
-  DEBUG ((EFI_D_INFO, "Capsule Free Memory from 0x%8X to 0x%8X\n", (UINTN) FreeMemBase, (UINTN) FreeMemBase + FreeMemSize));\r
-\r
-  //\r
-  // Relocate all the block descriptors to low memory to make further\r
-  // processing easier.\r
-  //\r
-  BlockList = RelocateBlockDescriptors (PeiServices, BlockList, FreeMemBase, FreeMemSize);\r
-  if (BlockList == NULL) {\r
+#ifdef MDE_CPU_IA32\r
+  if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
     //\r
-    // Not enough room to relocate the descriptors\r
+    // Switch to 64-bit mode to process capsule data when:\r
+    // 1. When DXE phase is 64-bit\r
+    // 2. When the buffer for 64-bit transition exists\r
+    // 3. When Capsule X64 image is built in BIOS image\r
+    // In 64-bit mode, we can process capsule data above 4GB.\r
     //\r
-    return EFI_BUFFER_TOO_SMALL;\r
-  }\r
-\r
-  //\r
-  // Take the top of memory for the capsule. Naturally align.\r
-  //\r
-  DestPtr         = FreeMemBase + FreeMemSize - CapsuleSize;\r
-  DestPtr         = (UINT8 *) ((UINTN) DestPtr &~ (UINTN) (sizeof (UINTN) - 1));\r
-  FreeMemBase     = (UINT8 *) BlockList + DescriptorsSize;\r
-  FreeMemSize     = FreeMemSize - DescriptorsSize - CapsuleSize;\r
-  NewCapsuleBase  = (VOID *) DestPtr;\r
-\r
-  //\r
-  // Move all the blocks to the top (high) of memory.\r
-  // Relocate all the obstructing blocks. Note that the block descriptors\r
-  // were coalesced when they were relocated, so we can just ++ the pointer.\r
-  //\r
-  CurrentBlockDesc = BlockList;\r
-  while ((CurrentBlockDesc->Length != 0) || (CurrentBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {\r
-    //\r
-    // See if any of the remaining capsule blocks are in the way\r
-    //\r
-    TempBlockDesc = CurrentBlockDesc;\r
-    while (TempBlockDesc->Length != 0) {\r
-      //\r
-      // Is this block in the way of where we want to copy the current descriptor to?\r
-      //\r
-      if (IsOverlapped (\r
-            (UINT8 *) DestPtr,\r
-            (UINTN) CurrentBlockDesc->Length,\r
-            (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,\r
-            (UINTN) TempBlockDesc->Length\r
-            )) {\r
-        //\r
-        // Relocate the block\r
-        //\r
-        RelocPtr = FindFreeMem (BlockList, FreeMemBase, FreeMemSize, (UINTN) TempBlockDesc->Length);\r
-        if (RelocPtr == NULL) {\r
-          return EFI_BUFFER_TOO_SMALL;\r
-        }\r
-\r
-        CopyMem ((VOID *) RelocPtr, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);\r
-        DEBUG ((EFI_D_INFO, "Capsule reloc data block from 0x%8X to 0x%8X with size 0x%8X\n",\r
-                (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) RelocPtr, (UINTN) TempBlockDesc->Length));\r
-\r
-        TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocPtr;\r
-      }\r
-      //\r
-      // Next descriptor\r
-      //\r
-      TempBlockDesc++;\r
+    CoalesceImageEntryPoint = 0;\r
+    Status = GetLongModeContext (&LongModeBuffer);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "Fail to find the variables for long mode context!\n"));\r
+      Status = EFI_NOT_FOUND;\r
+      goto Done;\r
     }\r
-    //\r
-    // Ok, we made it through. Copy the block.\r
-    // we just support greping one capsule from the lists of block descs list.\r
-    //\r
-    CapsuleTimes ++;\r
-    //\r
-    //Skip the first block descriptor that filled with EFI_CAPSULE_PEIM_PRIVATE_DATA\r
-    //\r
-    if (CapsuleTimes > 1) {\r
-      //\r
-      //For every capsule entry point, check its header to determine whether to relocate it.\r
-      //If it is invalid, skip it and move on to the next capsule. If it is valid, relocate it.\r
-      //\r
-      if (CapsuleBeginFlag) {\r
-        CapsuleBeginFlag  = FALSE;\r
-        CapsuleHeader     = (EFI_CAPSULE_HEADER*)(UINTN)CurrentBlockDesc->Union.DataBlock;\r
-        SizeLeft          = CapsuleHeader->CapsuleImageSize;\r
-        if (!IsCapsuleCorrupted (CapsuleHeader)) {\r
-\r
-          if (CapsuleIndex > (MAX_SUPPORT_CAPSULE_NUM - 1)) {\r
-            DEBUG ((EFI_D_ERROR, "Capsule number exceeds the max number of %d!\n", MAX_SUPPORT_CAPSULE_NUM));\r
-            return  EFI_BUFFER_TOO_SMALL;\r
-          }\r
-\r
-          //\r
-          // Relocate this valid capsule\r
-          //\r
-          IsCorrupted  = FALSE;\r
-          CapsuleImageSize += SizeLeft;\r
-          CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);\r
-          DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes,\r
-                 (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));\r
-          //\r
-          // Cache the begin offset of this capsule\r
-          //\r
-          CapsuleOffset[CapsuleIndex++] = (UINT32) (UINTN) DestPtr - (UINT32)(UINTN)NewCapsuleBase - (UINT32)sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA);\r
-          DestPtr += CurrentBlockDesc->Length;\r
-        }\r
-        //\r
-        // If the current block length is greater than or equal to SizeLeft, this is the \r
-        // start of the next capsule\r
-        //\r
-        if (CurrentBlockDesc->Length < SizeLeft) {\r
-          SizeLeft -= CurrentBlockDesc->Length;\r
-        } else {\r
-          //\r
-          // Start the next cycle\r
-          //\r
-          SizeLeft         = 0;\r
-          IsCorrupted      = TRUE;\r
-          CapsuleBeginFlag = TRUE;          \r
-        }\r
-      } else {\r
-        //\r
-        //Go on relocating the current capule image.\r
-        //\r
-        if (CurrentBlockDesc->Length < SizeLeft) {\r
-          if (!IsCorrupted) {\r
-            CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);\r
-            DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes,\r
-                   (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));\r
-            DestPtr += CurrentBlockDesc->Length;\r
-          }\r
-          SizeLeft -= CurrentBlockDesc->Length;\r
-        } else {\r
-          //\r
-          //Here is the end of the current capsule image.\r
-          //\r
-          if (!IsCorrupted) {\r
-            CopyMem ((VOID *) DestPtr, (VOID *)(UINTN)(CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);\r
-            DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes,\r
-                   (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));\r
-            DestPtr += CurrentBlockDesc->Length;\r
-          }\r
-          //\r
-          // Start the next cycle\r
-          //\r
-          SizeLeft = 0;\r
-          IsCorrupted = TRUE;\r
-          CapsuleBeginFlag = TRUE; \r
-        }\r
-      }\r
-    } else {\r
-      //\r
-      //The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.\r
-      //\r
-      CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);\r
-      DestPtr += CurrentBlockDesc->Length;\r
+    \r
+    Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType);\r
+    if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) {\r
+      DEBUG ((EFI_D_ERROR, "Fail to find CapsuleX64 module in FV!\n"));\r
+      Status = EFI_NOT_FOUND;\r
+      goto Done;\r
     }\r
+    ASSERT (CoalesceImageEntryPoint != 0);\r
+    CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint;\r
+    Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);\r
+  } else {\r
     //\r
-    //Walk through the block descriptor list.\r
+    // Capsule is processed in IA32 mode.\r
     //\r
-    CurrentBlockDesc++;\r
+    Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);\r
   }\r
+#else\r
   //\r
-  // We return the base of memory we want reserved, and the size.\r
-  // The memory peim should handle it appropriately from there.\r
+  // Process capsule directly.\r
   //\r
-  *MemorySize = (UINTN) CapsuleImageSize;\r
-  *MemoryBase = (VOID *) NewCapsuleBase;\r
-\r
-  //\r
-  //Append the offsets of mutiply capsules to the continous buffer\r
-  //\r
-  DataPtr    = (VOID*)((UINTN)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (UINTN)CapsuleImageSize);\r
-  AddDataPtr = (UINT32*)(((UINTN) DataPtr + sizeof(UINT32) - 1) &~ (UINT32) (sizeof (UINT32) - 1));\r
-\r
-  *AddDataPtr++ = CapsuleIndex;\r
-\r
-  CopyMem (AddDataPtr, &CapsuleOffset[0], sizeof (UINT32) * CapsuleIndex);\r
+  Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);\r
+#endif\r
+  \r
+  DEBUG ((EFI_D_INFO, "Capsule Coalesce Status = %r!\n", Status));\r
 \r
-  PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase;\r
-  PrivateDataPtr->CapsuleSize = (UINTN)CapsuleImageSize;\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    DEBUG ((EFI_D_ERROR, "There is not enough memory to process capsule!\n"));\r
+  }\r
+  \r
+  if (Status == EFI_NOT_FOUND) {\r
+    DEBUG ((EFI_D_ERROR, "Fail to parse capsule descriptor in memory!\n"));\r
+    REPORT_STATUS_CODE (\r
+      EFI_ERROR_CODE | EFI_ERROR_MAJOR,\r
+      (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR)\r
+      );\r
+  }\r
 \r
+Done:\r
   return Status;\r
 }\r
 \r
@@ -1168,7 +861,10 @@ CapsuleTestPattern (
   // is, then test it now.\r
   //\r
   TestPtr = (UINT32 *) CapsuleBase;\r
-  if (*TestPtr == CAPSULE_TEST_SIGNATURE) {\r
+  //\r
+  // 0x54534554 "TEST"\r
+  //\r
+  if (*TestPtr == 0x54534554) {\r
     RetValue = TRUE;\r
     DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n"));\r
     TestSize = TestPtr[1] / sizeof (UINT32);\r
diff --git a/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c b/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c
new file mode 100644 (file)
index 0000000..c64fe10
--- /dev/null
@@ -0,0 +1,66 @@
+/** @file\r
+  The X64 entrypoint is used to process capsule in long mode.\r
+\r
+Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include "CommonHeader.h"\r
+\r
+/**\r
+  The X64 entrypoint is used to process capsule in long mode then\r
+  return to 32-bit protected mode.\r
+\r
+  @param  EntrypointContext   Pointer to the context of long mode.\r
+  @param  ReturnContext       Pointer to the context of 32-bit protected mode.\r
+\r
+  @retval This function should never return actually.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+_ModuleEntryPoint (\r
+  SWITCH_32_TO_64_CONTEXT       *EntrypointContext,\r
+  SWITCH_64_TO_32_CONTEXT       *ReturnContext\r
+)\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  //\r
+  // Call CapsuleDataCoalesce to process capsule.\r
+  //\r
+  Status = CapsuleDataCoalesce (\r
+             NULL,\r
+             (EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr,\r
+             (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr,\r
+             (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr\r
+             );\r
+  \r
+  ReturnContext->ReturnStatus = Status;\r
+\r
+  //\r
+  // Finish to coalesce capsule, and return to 32-bit mode.\r
+  //\r
+  AsmDisablePaging64 (\r
+    ReturnContext->ReturnCs,\r
+    (UINT32) ReturnContext->ReturnEntryPoint,\r
+    (UINT32) (UINTN) EntrypointContext,\r
+    (UINT32) (UINTN) ReturnContext,\r
+    (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength)\r
+    );  \r
+  \r
+  //\r
+  // Should never be here.\r
+  //\r
+  ASSERT (FALSE);\r
+  return EFI_SUCCESS;\r
+}
\ No newline at end of file
index 43388f55fcd693d045392ab4fb46fbd96b912da9..e52b6a5f28faf074122138dc35e9d1d445a748e6 100644 (file)
 [Sources]\r
   CapsuleService.c\r
 \r
+[Sources.Ia32, Sources.IPF, Sources.EBC, Sources.ARM]\r
+  SaveLongModeContext.c\r
+\r
+[Sources.X64]\r
+  X64/SaveLongModeContext.c\r
+\r
 [Packages]\r
   MdePkg/MdePkg.dec\r
   MdeModulePkg/MdeModulePkg.dec\r
   UefiRuntimeLib\r
   BaseLib\r
   PrintLib\r
+  \r
+[LibraryClasses.X64]\r
+  LockBoxLib\r
+  UefiLib\r
+  BaseMemoryLib\r
+  HobLib  \r
 \r
 [Guids]\r
   gEfiCapsuleVendorGuid                         ## SOMETIMES_PRODUCED (Process across reset capsule image) ## Variable:L"CapsuleUpdateData" for capsule updated data\r
 \r
+[Guids.X64]\r
+  gEfiAcpiVariableGuid                          # ALWAYS_CONSUMED\r
+  gEfiAcpiS3ContextGuid                         # ALWAYS_CONSUMED\r
+\r
 [Protocols]\r
   gEfiCapsuleArchProtocolGuid                   ## PRODUCED\r
 \r
+[Protocols.X64]\r
+  gEfiDxeSmmReadyToLockProtocolGuid             # ALWAYS_CONSUMED\r
+\r
 [FeaturePcd]\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset\r
 \r
+[FeaturePcd.X64]\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode\r
+\r
 [Pcd]\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizeNonPopulateCapsule\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizePopulateCapsule || gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset  ## Populate Image requires reset support.\r
 \r
+[Pcd.X64]\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCapsulePeiLongModeStackSize\r
+\r
 [Depex]\r
   gEfiVariableWriteArchProtocolGuid             ## Depends on variable write functionality to produce capsule data variable\r
index 76b2a2a8fcc525c8a9794573220152ed00e05bf8..17f167c978cad91e9d45d93dc52899b9f4efaaba 100644 (file)
@@ -4,7 +4,7 @@
   It installs the Capsule Architectural Protocol defined in PI1.0a to signify \r
   the capsule runtime services are ready.\r
 \r
-Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions of the BSD License\r
 which accompanies this distribution.  The full text of the license may be found at\r
@@ -40,6 +40,15 @@ EFI_HANDLE  mNewHandle = NULL;
 //\r
 UINTN       mTimes      = 0;\r
 \r
+/**\r
+  Create the variable to save the base address of page table and stack\r
+  for transferring into long mode in IA32 PEI.\r
+**/\r
+VOID\r
+SaveLongModeContext (\r
+  VOID\r
+  );\r
+\r
 /**\r
   Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended\r
   consumption, the firmware may process the capsule immediately. If the payload should persist\r
@@ -333,6 +342,15 @@ CapsuleServiceInitialize (
 {\r
   EFI_STATUS  Status;\r
   \r
+  //\r
+  // When PEI phase is IA32, DXE phase is X64, it is possible that capsule data are \r
+  // put above 4GB, so capsule PEI will transfer to long mode to get capsule data.\r
+  // The page table and stack is used to transfer processor mode from IA32 to long mode.\r
+  // Create the base address of page table and stack, and save them into variable.\r
+  // This is not needed when capsule with reset type is not supported.\r
+  //\r
+  SaveLongModeContext ();\r
+    \r
   //\r
   // Install capsule runtime services into UEFI runtime service tables.\r
   //\r
diff --git a/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c b/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c
new file mode 100644 (file)
index 0000000..72cea79
--- /dev/null
@@ -0,0 +1,27 @@
+/** @file\r
+  Create the NULL function to pass build in IA32/IPF/ARM/EBC.\r
+\r
+Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+/**\r
+  Only when PEI is IA32 and DXE is X64, we need transfer to long mode in PEI\r
+  in order to process capsule data above 4GB. So create a NULL function here for\r
+  other cases.\r
+**/\r
+VOID\r
+SaveLongModeContext (\r
+  VOID\r
+  )\r
+{\r
+}\r
diff --git a/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c b/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c
new file mode 100644 (file)
index 0000000..0cfc352
--- /dev/null
@@ -0,0 +1,224 @@
+/** @file\r
+  Create the variable to save the base address of page table and stack\r
+  for transferring into long mode in IA32 capsule PEI.\r
+\r
+Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/Capsule.h>\r
+#include <Protocol/DxeSmmReadyToLock.h>\r
+\r
+#include <Guid/CapsuleVendor.h>\r
+#include <Guid/AcpiS3Context.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiRuntimeLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/LockBoxLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/HobLib.h>\r
+\r
+/**\r
+  Allocate EfiACPIMemoryNVS below 4G memory address.\r
+\r
+  This function allocates EfiACPIMemoryNVS below 4G memory address.\r
+\r
+  @param  Size         Size of memory to allocate.\r
+  \r
+  @return Allocated address for output.\r
+\r
+**/\r
+VOID*\r
+AllocateAcpiNvsMemoryBelow4G (\r
+  IN   UINTN   Size\r
+  )\r
+{\r
+  UINTN                 Pages;\r
+  EFI_PHYSICAL_ADDRESS  Address;\r
+  EFI_STATUS            Status;\r
+  VOID*                 Buffer;\r
+\r
+  Pages = EFI_SIZE_TO_PAGES (Size);\r
+  Address = 0xffffffff;\r
+\r
+  Status  = gBS->AllocatePages (\r
+                   AllocateMaxAddress,\r
+                   EfiACPIMemoryNVS,\r
+                   Pages,\r
+                   &Address\r
+                   );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Buffer = (VOID *) (UINTN) Address;\r
+  ZeroMem (Buffer, Size);\r
+\r
+  return Buffer;\r
+}\r
+\r
+/**\r
+  DxeSmmReadyToLock Protocol notification event handler.\r
+  We reuse S3 ACPI NVS reserved memory to do capsule process\r
+  after reset.\r
+\r
+  @param[in] Event    Event whose notification function is being invoked.\r
+  @param[in] Context  Pointer to the notification function's context.\r
+**/\r
+VOID\r
+EFIAPI\r
+DxeSmmReadyToLockNotification (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  VOID                          *DxeSmmReadyToLock;\r
+  UINTN                         VarSize;\r
+  EFI_PHYSICAL_ADDRESS          TempAcpiS3Context;\r
+  ACPI_S3_CONTEXT               *AcpiS3Context;\r
+  EFI_CAPSULE_LONG_MODE_BUFFER  LongModeBuffer;\r
+  UINTN                         TotalPagesNum;\r
+  UINT8                         PhysicalAddressBits;\r
+  VOID                          *Hob;\r
+  UINT32                        NumberOfPml4EntriesNeeded;\r
+  UINT32                        NumberOfPdpEntriesNeeded;\r
+  BOOLEAN                       LockBoxFound;\r
+\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiDxeSmmReadyToLockProtocolGuid,\r
+                  NULL,\r
+                  &DxeSmmReadyToLock\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Get the ACPI NVS pages reserved by AcpiS3Save\r
+  //\r
+  LockBoxFound = FALSE;\r
+  VarSize = sizeof (EFI_PHYSICAL_ADDRESS);\r
+  Status = RestoreLockBox (\r
+             &gEfiAcpiVariableGuid,\r
+             &TempAcpiS3Context,\r
+             &VarSize\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context;\r
+    ASSERT (AcpiS3Context != NULL);\r
+    \r
+    Status = RestoreLockBox (\r
+               &gEfiAcpiS3ContextGuid,\r
+               NULL,\r
+               NULL\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      LongModeBuffer.PageTableAddress = AcpiS3Context->S3NvsPageTableAddress;\r
+      LongModeBuffer.StackBaseAddress = AcpiS3Context->BootScriptStackBase;\r
+      LongModeBuffer.StackSize        = AcpiS3Context->BootScriptStackSize;\r
+      LockBoxFound                    = TRUE;\r
+    }\r
+  }\r
+  \r
+  if (!LockBoxFound) {\r
+    //\r
+    // Page table base address and stack base address can not be found in lock box,\r
+    // allocate both here. \r
+    //\r
+\r
+    //\r
+    // Get physical address bits supported from CPU HOB.\r
+    //\r
+    PhysicalAddressBits = 36;\r
+    \r
+    Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
+    if (Hob != NULL) {\r
+      PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
+    }\r
+    \r
+    //\r
+    // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
+    //\r
+    ASSERT (PhysicalAddressBits <= 52);\r
+    if (PhysicalAddressBits > 48) {\r
+      PhysicalAddressBits = 48;\r
+    }\r
+    \r
+    //\r
+    // Calculate page table size and allocate memory for it.\r
+    //\r
+    if (PhysicalAddressBits <= 39 ) {\r
+      NumberOfPml4EntriesNeeded = 1;\r
+      NumberOfPdpEntriesNeeded =  1 << (PhysicalAddressBits - 30);\r
+    } else {\r
+      NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);\r
+      NumberOfPdpEntriesNeeded = 512;\r
+    }\r
+    \r
+    TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;\r
+    LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));\r
+    ASSERT (LongModeBuffer.PageTableAddress != 0);\r
+    \r
+    //\r
+    // Allocate stack\r
+    //\r
+    LongModeBuffer.StackSize        = PcdGet32 (PcdCapsulePeiLongModeStackSize);\r
+    LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));\r
+    ASSERT (LongModeBuffer.StackBaseAddress != 0);\r
+  }\r
+\r
+  Status = gRT->SetVariable (\r
+                  EFI_CAPSULE_LONG_MODE_BUFFER_NAME,\r
+                  &gEfiCapsuleVendorGuid,\r
+                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+                  sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),\r
+                  &LongModeBuffer\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Close event, so it will not be invoked again.\r
+  //\r
+  gBS->CloseEvent (Event);\r
+\r
+  return ;\r
+}\r
+\r
+/**\r
+  Create the variable to save the base address of page table and stack\r
+  for transferring into long mode in IA32 capsule PEI.\r
+**/\r
+VOID\r
+SaveLongModeContext (\r
+  VOID\r
+  )\r
+{\r
+  VOID        *Registration;\r
+  \r
+  if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {\r
+    //\r
+    // Register event to get ACPI NVS pages reserved from lock box, these pages will be used by\r
+    // Capsule IA32 PEI to transfer to long mode to access capsule above 4GB.\r
+    //\r
+    EfiCreateProtocolNotifyEvent (\r
+      &gEfiDxeSmmReadyToLockProtocolGuid,\r
+      TPL_CALLBACK,\r
+      DxeSmmReadyToLockNotification,\r
+      NULL,\r
+      &Registration\r
+      );\r
+  }\r
+}\r