summaryrefslogtreecommitdiff
path: root/Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c
diff options
context:
space:
mode:
Diffstat (limited to 'Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c')
-rw-r--r--Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c1254
1 files changed, 1254 insertions, 0 deletions
diff --git a/Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c b/Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c
new file mode 100644
index 0000000000..d789fbbc75
--- /dev/null
+++ b/Core/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c
@@ -0,0 +1,1254 @@
+/** @file
+ The logic to process capsule.
+
+ Caution: This module requires additional review when modified.
+ This driver will have external input - capsule image.
+ This external input must be validated carefully to avoid security issue like
+ buffer overflow, integer overflow.
+
+ CapsuleDataCoalesce() will do basic validation before coalesce capsule data
+ into memory.
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Uefi.h>
+#include <PiPei.h>
+
+#include <Guid/CapsuleVendor.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+#include <Library/BaseLib.h>
+
+#include "CommonHeader.h"
+
+#define MIN_COALESCE_ADDR (1024 * 1024)
+
+/**
+ Given a pointer to the capsule block list, info on the available system
+ memory, and the size of a buffer, find a free block of memory where a
+ buffer of the given size can be copied to safely.
+
+ @param BlockList Pointer to head of capsule block descriptors
+ @param MemBase Pointer to the base of memory in which we want to find free space
+ @param MemSize The size of the block of memory pointed to by MemBase
+ @param DataSize How big a free block we want to find
+
+ @return A pointer to a memory block of at least DataSize that lies somewhere
+ between MemBase and (MemBase + MemSize). The memory pointed to does not
+ contain any of the capsule block descriptors or capsule blocks pointed to
+ by the BlockList.
+
+**/
+UINT8 *
+FindFreeMem (
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ UINT8 *MemBase,
+ UINTN MemSize,
+ UINTN DataSize
+ );
+
+/**
+ Check the integrity of the capsule descriptors.
+
+ @param BlockList Pointer to the capsule descriptors
+
+ @retval NULL BlockList is not valid.
+ @retval LastBlockDesc Last one Block in BlockList
+
+**/
+EFI_CAPSULE_BLOCK_DESCRIPTOR *
+ValidateCapsuleIntegrity (
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList
+ );
+
+/**
+ The capsule block descriptors may be fragmented and spread all over memory.
+ To simplify the coalescing of capsule blocks, first coalesce all the
+ capsule block descriptors low in memory.
+
+ The descriptors passed in can be fragmented throughout memory. Here
+ they are relocated into memory to turn them into a contiguous (null
+ terminated) array.
+
+ @param PeiServices pointer to PEI services table
+ @param BlockList pointer to the capsule block descriptors
+ @param NumDescriptors number of capsule data block descriptors, whose Length is non-zero.
+ @param MemBase base of system memory in which we can work
+ @param MemSize size of the system memory pointed to by MemBase
+
+ @retval NULL could not relocate the descriptors
+ @retval Pointer to the base of the successfully-relocated block descriptors.
+
+**/
+EFI_CAPSULE_BLOCK_DESCRIPTOR *
+RelocateBlockDescriptors (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ IN UINTN NumDescriptors,
+ IN UINT8 *MemBase,
+ IN UINTN MemSize
+ );
+
+/**
+ Check every capsule header.
+
+ @param CapsuleHeader The pointer to EFI_CAPSULE_HEADER
+
+ @retval FALSE Capsule is OK
+ @retval TRUE Capsule is corrupted
+
+**/
+BOOLEAN
+IsCapsuleCorrupted (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader
+ );
+
+/**
+ Determine if two buffers overlap in memory.
+
+ @param Buff1 pointer to first buffer
+ @param Size1 size of Buff1
+ @param Buff2 pointer to second buffer
+ @param Size2 size of Buff2
+
+ @retval TRUE Buffers overlap in memory.
+ @retval FALSE Buffer doesn't overlap.
+
+**/
+BOOLEAN
+IsOverlapped (
+ UINT8 *Buff1,
+ UINTN Size1,
+ UINT8 *Buff2,
+ UINTN Size2
+ );
+
+/**
+ Given a pointer to a capsule block descriptor, traverse the list to figure
+ out how many legitimate descriptors there are, and how big the capsule it
+ refers to is.
+
+ @param Desc Pointer to the capsule block descriptors
+ @param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero.
+ @param CapsuleSize Optional pointer to where to return the capsule image size
+ @param CapsuleNumber Optional pointer to where to return the number of capsule
+
+ @retval EFI_NOT_FOUND No descriptors containing data in the list
+ @retval EFI_SUCCESS Return data is valid
+
+**/
+EFI_STATUS
+GetCapsuleInfo (
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc,
+ IN OUT UINTN *NumDescriptors OPTIONAL,
+ IN OUT UINTN *CapsuleSize OPTIONAL,
+ IN OUT UINTN *CapsuleNumber OPTIONAL
+ );
+
+/**
+ Given a pointer to the capsule block list, info on the available system
+ memory, and the size of a buffer, find a free block of memory where a
+ buffer of the given size can be copied to safely.
+
+ @param BlockList Pointer to head of capsule block descriptors
+ @param MemBase Pointer to the base of memory in which we want to find free space
+ @param MemSize The size of the block of memory pointed to by MemBase
+ @param DataSize How big a free block we want to find
+
+ @return A pointer to a memory block of at least DataSize that lies somewhere
+ between MemBase and (MemBase + MemSize). The memory pointed to does not
+ contain any of the capsule block descriptors or capsule blocks pointed to
+ by the BlockList.
+
+**/
+UINT8 *
+FindFreeMem (
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ UINT8 *MemBase,
+ UINTN MemSize,
+ UINTN DataSize
+ )
+{
+ UINTN Size;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempDesc;
+ UINT8 *MemEnd;
+ BOOLEAN Failed;
+
+ //
+ // Need at least enough to copy the data to at the end of the buffer, so
+ // say the end is less the data size for easy comparisons here.
+ //
+ MemEnd = MemBase + MemSize - DataSize;
+ CurrDesc = BlockList;
+ //
+ // Go through all the descriptor blocks and see if any obstruct the range
+ //
+ while (CurrDesc != NULL) {
+ //
+ // Get the size of this block list and see if it's in the way
+ //
+ Failed = FALSE;
+ TempDesc = CurrDesc;
+ Size = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ while (TempDesc->Length != 0) {
+ Size += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ TempDesc++;
+ }
+
+ if (IsOverlapped (MemBase, DataSize, (UINT8 *) CurrDesc, Size)) {
+ //
+ // Set our new base to the end of this block list and start all over
+ //
+ MemBase = (UINT8 *) CurrDesc + Size;
+ CurrDesc = BlockList;
+ if (MemBase > MemEnd) {
+ return NULL;
+ }
+
+ Failed = TRUE;
+ }
+ //
+ // Now go through all the blocks and make sure none are in the way
+ //
+ while ((CurrDesc->Length != 0) && (!Failed)) {
+ if (IsOverlapped (MemBase, DataSize, (UINT8 *) (UINTN) CurrDesc->Union.DataBlock, (UINTN) CurrDesc->Length)) {
+ //
+ // Set our new base to the end of this block and start all over
+ //
+ Failed = TRUE;
+ MemBase = (UINT8 *) ((UINTN) CurrDesc->Union.DataBlock) + CurrDesc->Length;
+ CurrDesc = BlockList;
+ if (MemBase > MemEnd) {
+ return NULL;
+ }
+ }
+ CurrDesc++;
+ }
+ //
+ // Normal continuation -- jump to next block descriptor list
+ //
+ if (!Failed) {
+ CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) CurrDesc->Union.ContinuationPointer;
+ }
+ }
+ return MemBase;
+}
+
+/**
+ Check the integrity of the capsule descriptors.
+
+ @param BlockList Pointer to the capsule descriptors
+
+ @retval NULL BlockList is not valid.
+ @retval LastBlockDesc Last one Block in BlockList
+
+**/
+EFI_CAPSULE_BLOCK_DESCRIPTOR *
+ValidateCapsuleIntegrity (
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList
+ )
+{
+ EFI_CAPSULE_HEADER *CapsuleHeader;
+ UINT64 CapsuleSize;
+ UINTN CapsuleCount;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *Ptr;
+
+ DEBUG ((EFI_D_INFO, "ValidateCapsuleIntegrity\n"));
+
+ //
+ // Go through the list to look for inconsistencies. Check for:
+ // * misaligned block descriptors.
+ // * The first capsule header guid
+ // * The first capsule header flag
+ // * The first capsule header HeaderSize
+ // * Length > MAX_ADDRESS
+ // * ContinuationPointer > MAX_ADDRESS
+ // * DataBlock + Length > MAX_ADDRESS
+ //
+ CapsuleSize = 0;
+ CapsuleCount = 0;
+ Ptr = BlockList;
+
+ DEBUG ((EFI_D_INFO, "Ptr - 0x%x\n", Ptr));
+ DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
+ DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
+ while ((Ptr->Length != 0) || (Ptr->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ //
+ // Make sure the descriptor is aligned at UINT64 in memory
+ //
+ if ((UINTN) Ptr & (sizeof(UINT64) - 1)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: BlockList address failed alignment check\n"));
+ return NULL;
+ }
+ //
+ // Sanity Check
+ //
+ if (Ptr->Length > MAX_ADDRESS) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Length(0x%lx) > MAX_ADDRESS\n", Ptr->Length));
+ return NULL;
+ }
+
+ if (Ptr->Length == 0) {
+ //
+ // Sanity Check
+ //
+ if (Ptr->Union.ContinuationPointer > MAX_ADDRESS) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Union.ContinuationPointer(0x%lx) > MAX_ADDRESS\n", Ptr->Union.ContinuationPointer));
+ return NULL;
+ }
+ //
+ // Descriptor points to another list of block descriptors somewhere
+ // else.
+ //
+ Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Ptr->Union.ContinuationPointer;
+ DEBUG ((EFI_D_INFO, "Ptr(C) - 0x%x\n", Ptr));
+ DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
+ DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
+ } else {
+ //
+ // Sanity Check
+ //
+ if (Ptr->Union.DataBlock > (MAX_ADDRESS - (UINTN)Ptr->Length)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Union.DataBlock(0x%lx) > (MAX_ADDRESS - (UINTN)Ptr->Length(0x%lx))\n", Ptr->Union.DataBlock, Ptr->Length));
+ return NULL;
+ }
+
+ //
+ //To enhance the reliability of check-up, the first capsule's header is checked here.
+ //More reliabilities check-up will do later.
+ //
+ if (CapsuleSize == 0) {
+ //
+ //Move to the first capsule to check its header.
+ //
+ CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Ptr->Union.DataBlock);
+ //
+ // Sanity check
+ //
+ if (Ptr->Length < sizeof(EFI_CAPSULE_HEADER)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Length(0x%lx) < sizeof(EFI_CAPSULE_HEADER)\n", Ptr->Length));
+ return NULL;
+ }
+ //
+ // Make sure HeaderSize field is valid
+ //
+ if (CapsuleHeader->HeaderSize > CapsuleHeader->CapsuleImageSize) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleHeader->HeaderSize(0x%x) > CapsuleHeader->CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize));
+ return NULL;
+ }
+ if (IsCapsuleCorrupted (CapsuleHeader)) {
+ return NULL;
+ }
+ CapsuleCount ++;
+ CapsuleSize = CapsuleHeader->CapsuleImageSize;
+ }
+
+ if (CapsuleSize >= Ptr->Length) {
+ CapsuleSize = CapsuleSize - Ptr->Length;
+ } else {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize(0x%lx) < Ptr->Length(0x%lx)\n", CapsuleSize, Ptr->Length));
+ //
+ // Sanity check
+ //
+ return NULL;
+ }
+
+ //
+ // Move to next BLOCK descriptor
+ //
+ Ptr++;
+ DEBUG ((EFI_D_INFO, "Ptr(B) - 0x%x\n", Ptr));
+ DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
+ DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
+ }
+ }
+
+ if (CapsuleCount == 0) {
+ //
+ // No any capsule is found in BlockList
+ //
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleCount(0x%x) == 0\n", CapsuleCount));
+ return NULL;
+ }
+
+ if (CapsuleSize != 0) {
+ //
+ // Capsule data is incomplete.
+ //
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize(0x%lx) != 0\n", CapsuleSize));
+ return NULL;
+ }
+
+ return Ptr;
+}
+
+/**
+ The capsule block descriptors may be fragmented and spread all over memory.
+ To simplify the coalescing of capsule blocks, first coalesce all the
+ capsule block descriptors low in memory.
+
+ The descriptors passed in can be fragmented throughout memory. Here
+ they are relocated into memory to turn them into a contiguous (null
+ terminated) array.
+
+ @param PeiServices pointer to PEI services table
+ @param BlockList pointer to the capsule block descriptors
+ @param NumDescriptors number of capsule data block descriptors, whose Length is non-zero.
+ @param MemBase base of system memory in which we can work
+ @param MemSize size of the system memory pointed to by MemBase
+
+ @retval NULL could not relocate the descriptors
+ @retval Pointer to the base of the successfully-relocated block descriptors.
+
+**/
+EFI_CAPSULE_BLOCK_DESCRIPTOR *
+RelocateBlockDescriptors (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
+ IN UINTN NumDescriptors,
+ IN UINT8 *MemBase,
+ IN UINTN MemSize
+ )
+{
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *NewBlockList;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrBlockDescHead;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *PrevBlockDescTail;
+ UINTN BufferSize;
+ UINT8 *RelocBuffer;
+ UINTN BlockListSize;
+
+ //
+ // Get the info on the blocks and descriptors. Since we're going to move
+ // the descriptors low in memory, adjust the base/size values accordingly here.
+ // NumDescriptors is the number of legit data descriptors, so add one for
+ // a terminator. (Already done by caller, no check is needed.)
+ //
+
+ BufferSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ NewBlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) MemBase;
+ if (MemSize < BufferSize) {
+ return NULL;
+ }
+
+ MemSize -= BufferSize;
+ MemBase += BufferSize;
+ //
+ // Go through all the blocks and make sure none are in the way
+ //
+ TempBlockDesc = BlockList;
+ while (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
+ if (TempBlockDesc->Length == 0) {
+ //
+ // Next block of descriptors
+ //
+ TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
+ } else {
+ //
+ // If the capsule data pointed to by this descriptor is in the way,
+ // move it.
+ //
+ if (IsOverlapped (
+ (UINT8 *) NewBlockList,
+ BufferSize,
+ (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,
+ (UINTN) TempBlockDesc->Length
+ )) {
+ //
+ // Relocate the block
+ //
+ RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, (UINTN) TempBlockDesc->Length);
+ if (RelocBuffer == NULL) {
+ return NULL;
+ }
+
+ CopyMem ((VOID *) RelocBuffer, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);
+ DEBUG ((EFI_D_INFO, "Capsule relocate descriptors from/to/size 0x%lX 0x%lX 0x%lX\n", TempBlockDesc->Union.DataBlock, (UINT64)(UINTN)RelocBuffer, TempBlockDesc->Length));
+ TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;
+ }
+ TempBlockDesc++;
+ }
+ }
+ //
+ // Now go through all the block descriptors to make sure that they're not
+ // in the memory region we want to copy them to.
+ //
+ CurrBlockDescHead = BlockList;
+ PrevBlockDescTail = NULL;
+ while ((CurrBlockDescHead != NULL) && (CurrBlockDescHead->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ //
+ // Get the size of this list then see if it overlaps our low region
+ //
+ TempBlockDesc = CurrBlockDescHead;
+ BlockListSize = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ while (TempBlockDesc->Length != 0) {
+ BlockListSize += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ TempBlockDesc++;
+ }
+
+ if (IsOverlapped (
+ (UINT8 *) NewBlockList,
+ BufferSize,
+ (UINT8 *) CurrBlockDescHead,
+ BlockListSize
+ )) {
+ //
+ // Overlaps, so move it out of the way
+ //
+ RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, BlockListSize);
+ if (RelocBuffer == NULL) {
+ return NULL;
+ }
+ CopyMem ((VOID *) RelocBuffer, (VOID *) CurrBlockDescHead, BlockListSize);
+ DEBUG ((EFI_D_INFO, "Capsule reloc descriptor block #2\n"));
+ //
+ // Point the previous block's next point to this copied version. If
+ // the tail pointer is null, then this is the first descriptor block.
+ //
+ if (PrevBlockDescTail == NULL) {
+ BlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) RelocBuffer;
+ } else {
+ PrevBlockDescTail->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;
+ }
+ }
+ //
+ // Save our new tail and jump to the next block list
+ //
+ PrevBlockDescTail = TempBlockDesc;
+ CurrBlockDescHead = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
+ }
+ //
+ // Cleared out low memory. Now copy the descriptors down there.
+ //
+ TempBlockDesc = BlockList;
+ CurrBlockDescHead = NewBlockList;
+ while ((TempBlockDesc != NULL) && (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ if (TempBlockDesc->Length != 0) {
+ CurrBlockDescHead->Union.DataBlock = TempBlockDesc->Union.DataBlock;
+ CurrBlockDescHead->Length = TempBlockDesc->Length;
+ CurrBlockDescHead++;
+ TempBlockDesc++;
+ } else {
+ TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
+ }
+ }
+ //
+ // Null terminate
+ //
+ CurrBlockDescHead->Union.ContinuationPointer = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL;
+ CurrBlockDescHead->Length = 0;
+ return NewBlockList;
+}
+
+/**
+ Determine if two buffers overlap in memory.
+
+ @param Buff1 pointer to first buffer
+ @param Size1 size of Buff1
+ @param Buff2 pointer to second buffer
+ @param Size2 size of Buff2
+
+ @retval TRUE Buffers overlap in memory.
+ @retval FALSE Buffer doesn't overlap.
+
+**/
+BOOLEAN
+IsOverlapped (
+ UINT8 *Buff1,
+ UINTN Size1,
+ UINT8 *Buff2,
+ UINTN Size2
+ )
+{
+ //
+ // If buff1's end is less than the start of buff2, then it's ok.
+ // Also, if buff1's start is beyond buff2's end, then it's ok.
+ //
+ if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Given a pointer to a capsule block descriptor, traverse the list to figure
+ out how many legitimate descriptors there are, and how big the capsule it
+ refers to is.
+
+ @param Desc Pointer to the capsule block descriptors
+ @param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero.
+ @param CapsuleSize Optional pointer to where to return the capsule image size
+ @param CapsuleNumber Optional pointer to where to return the number of capsule
+
+ @retval EFI_NOT_FOUND No descriptors containing data in the list
+ @retval EFI_SUCCESS Return data is valid
+
+**/
+EFI_STATUS
+GetCapsuleInfo (
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc,
+ IN OUT UINTN *NumDescriptors OPTIONAL,
+ IN OUT UINTN *CapsuleSize OPTIONAL,
+ IN OUT UINTN *CapsuleNumber OPTIONAL
+ )
+{
+ UINTN Count;
+ UINTN Size;
+ UINTN Number;
+ UINTN ThisCapsuleImageSize;
+ EFI_CAPSULE_HEADER *CapsuleHeader;
+
+ DEBUG ((EFI_D_INFO, "GetCapsuleInfo enter\n"));
+
+ ASSERT (Desc != NULL);
+
+ Count = 0;
+ Size = 0;
+ Number = 0;
+ ThisCapsuleImageSize = 0;
+
+ while (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
+ if (Desc->Length == 0) {
+ //
+ // Descriptor points to another list of block descriptors somewhere
+ //
+ Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
+ } else {
+ //
+ // Sanity Check
+ // It is needed, because ValidateCapsuleIntegrity() only validate one individual capsule Size.
+ // While here we need check all capsules size.
+ //
+ if (Desc->Length >= (MAX_ADDRESS - Size)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Desc->Length(0x%lx) >= (MAX_ADDRESS - Size(0x%x))\n", Desc->Length, Size));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Size += (UINTN) Desc->Length;
+ Count++;
+
+ //
+ // See if this is first capsule's header
+ //
+ if (ThisCapsuleImageSize == 0) {
+ CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Desc->Union.DataBlock);
+ //
+ // This has been checked in ValidateCapsuleIntegrity()
+ //
+ Number ++;
+ ThisCapsuleImageSize = CapsuleHeader->CapsuleImageSize;
+ }
+
+ //
+ // This has been checked in ValidateCapsuleIntegrity()
+ //
+ ASSERT (ThisCapsuleImageSize >= Desc->Length);
+ ThisCapsuleImageSize = (UINTN)(ThisCapsuleImageSize - Desc->Length);
+
+ //
+ // Move to next
+ //
+ Desc++;
+ }
+ }
+ //
+ // If no descriptors, then fail
+ //
+ if (Count == 0) {
+ DEBUG ((EFI_D_ERROR, "ERROR: Count == 0\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // checked in ValidateCapsuleIntegrity()
+ //
+ ASSERT (ThisCapsuleImageSize == 0);
+
+ if (NumDescriptors != NULL) {
+ *NumDescriptors = Count;
+ }
+
+ if (CapsuleSize != NULL) {
+ *CapsuleSize = Size;
+ }
+
+ if (CapsuleNumber != NULL) {
+ *CapsuleNumber = Number;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check every capsule header.
+
+ @param CapsuleHeader The pointer to EFI_CAPSULE_HEADER
+
+ @retval FALSE Capsule is OK
+ @retval TRUE Capsule is corrupted
+
+**/
+BOOLEAN
+IsCapsuleCorrupted (
+ IN EFI_CAPSULE_HEADER *CapsuleHeader
+ )
+{
+ //
+ //A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET.
+ //
+ if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {
+ return TRUE;
+ }
+ //
+ //Make sure the flags combination is supported by the platform.
+ //
+ if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
+ return TRUE;
+ }
+ if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Try to verify the integrity of a capsule test pattern before the
+ capsule gets coalesced. This can be useful in narrowing down
+ where capsule data corruption occurs.
+
+ The test pattern mode fills in memory with a counting UINT32 value.
+ If the capsule is not divided up in a multiple of 4-byte blocks, then
+ things get messy doing the check. Therefore there are some cases
+ here where we just give up and skip the pre-coalesce check.
+
+ @param PeiServices PEI services table
+ @param Desc Pointer to capsule descriptors
+**/
+VOID
+CapsuleTestPatternPreCoalesce (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc
+ )
+{
+ UINT32 *TestPtr;
+ UINT32 TestCounter;
+ UINT32 TestSize;
+
+ DEBUG ((EFI_D_INFO, "CapsuleTestPatternPreCoalesce\n"));
+
+ //
+ // Find first data descriptor
+ //
+ while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
+ }
+
+ if (Desc->Union.ContinuationPointer == 0) {
+ return ;
+ }
+ //
+ // First one better be long enough to at least hold the test signature
+ //
+ if (Desc->Length < sizeof (UINT32)) {
+ DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #1\n"));
+ return ;
+ }
+
+ TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;
+ //
+ // 0x54534554 "TEST"
+ //
+ if (*TestPtr != 0x54534554) {
+ return ;
+ }
+
+ TestCounter = 0;
+ TestSize = (UINT32) Desc->Length - 2 * sizeof (UINT32);
+ //
+ // Skip over the signature and the size fields in the pattern data header
+ //
+ TestPtr += 2;
+ while (1) {
+ if ((TestSize & 0x03) != 0) {
+ DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #2\n"));
+ return ;
+ }
+
+ while (TestSize > 0) {
+ if (*TestPtr != TestCounter) {
+ DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce failed data corruption check\n"));
+ return ;
+ }
+
+ TestSize -= sizeof (UINT32);
+ TestCounter++;
+ TestPtr++;
+ }
+ Desc++;
+ while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
+ }
+
+ if (Desc->Union.ContinuationPointer == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
+ return ;
+ }
+ TestSize = (UINT32) Desc->Length;
+ TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;
+ }
+}
+
+/**
+ Checks for the presence of capsule descriptors.
+ Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
+
+ @param BlockListBuffer Pointer to the buffer of capsule descriptors variables
+ @param BlockDescriptorList Pointer to the capsule descriptors list
+
+ @retval EFI_SUCCESS a valid capsule is present
+ @retval EFI_NOT_FOUND if a valid capsule is not present
+**/
+EFI_STATUS
+BuildCapsuleDescriptors (
+ IN EFI_PHYSICAL_ADDRESS *BlockListBuffer,
+ OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptorList
+ )
+{
+ UINTN Index;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *LastBlock;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlock;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *HeadBlock;
+
+ DEBUG ((EFI_D_INFO, "BuildCapsuleDescriptors enter\n"));
+
+ LastBlock = NULL;
+ HeadBlock = NULL;
+ TempBlock = NULL;
+ Index = 0;
+
+ while (BlockListBuffer[Index] != 0) {
+ //
+ // Test integrity of descriptors.
+ //
+ if (BlockListBuffer[Index] < MAX_ADDRESS) {
+ TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]);
+ if (TempBlock != NULL) {
+ if (LastBlock == NULL) {
+ LastBlock = TempBlock;
+
+ //
+ // Return the base of the block descriptors
+ //
+ HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index];
+ } else {
+ //
+ // Combine the different BlockList into single BlockList.
+ //
+ LastBlock->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)BlockListBuffer[Index];
+ LastBlock->Length = 0;
+ LastBlock = TempBlock;
+ }
+ }
+ } else {
+ DEBUG ((EFI_D_ERROR, "ERROR: BlockListBuffer[Index](0x%lx) < MAX_ADDRESS\n", BlockListBuffer[Index]));
+ }
+ Index ++;
+ }
+
+ if (HeadBlock != NULL) {
+ *BlockDescriptorList = HeadBlock;
+ return EFI_SUCCESS;
+ }
+ return EFI_NOT_FOUND;
+}
+
+/**
+ The function to coalesce a fragmented capsule in memory.
+
+ Memory Map for coalesced capsule:
+ MemBase + ---->+---------------------------+<-----------+
+ MemSize | ------------------------- | |
+ | | Capsule [Num-1] | | |
+ | ------------------------- | |
+ | | ................ | | |
+ | ------------------------- | |
+ | | Capsule [1] | | |
+ | ------------------------- | |
+ | | Capsule [0] | | |
+ | ------------------------- | |
+ | Capsule Image | |
+CapsuleImageBase-->+---------------------------+
+ | ------------------------- | |
+ | | CapsuleOffset[Num-1] | | |
+ | ------------------------- | |
+ | | ................ | | CapsuleSize
+ | ------------------------- | |
+ | | CapsuleOffset[1] | | |
+ | ------------------------- | |
+ | | CapsuleOffset[0] | | |
+ |---------------------------| |
+ | | CapsuleNumber | | |
+ | ------------------------- | |
+ | | CapsuleAllImageSize | | |
+ | ------------------------- | |
+ | PrivateData | |
+ DestPtr ---->+---------------------------+<-----------+
+ | | |
+ | FreeMem | FreeMemSize
+ | | |
+ FreeMemBase --->+---------------------------+<-----------+
+ | Terminator |
+ +---------------------------+
+ | BlockDescriptor n |
+ +---------------------------+
+ | ................. |
+ +---------------------------+
+ | BlockDescriptor 1 |
+ +---------------------------+
+ | BlockDescriptor 0 |
+ +---------------------------+
+ | PrivateDataDesc 0 |
+ MemBase ---->+---------------------------+<----- BlockList
+
+ Caution: This function may receive untrusted input.
+ The capsule data is external input, so this routine will do basic validation before
+ coalesce capsule data into memory.
+
+ @param PeiServices General purpose services available to every PEIM.
+ @param BlockListBuffer Point to the buffer of Capsule Descriptor Variables.
+ @param MemoryBase Pointer to the base of a block of memory that we can walk
+ all over while trying to coalesce our buffers.
+ On output, this variable will hold the base address of
+ a coalesced capsule.
+ @param MemorySize Size of the memory region pointed to by MemoryBase.
+ On output, this variable will contain the size of the
+ coalesced capsule.
+
+ @retval EFI_NOT_FOUND If we could not find the capsule descriptors.
+
+ @retval EFI_BUFFER_TOO_SMALL
+ If we could not coalesce the capsule in the memory
+ region provided to us.
+
+ @retval EFI_SUCCESS Processed the capsule successfully.
+**/
+EFI_STATUS
+EFIAPI
+CapsuleDataCoalesce (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PHYSICAL_ADDRESS *BlockListBuffer,
+ IN OUT VOID **MemoryBase,
+ IN OUT UINTN *MemorySize
+ )
+{
+ VOID *NewCapsuleBase;
+ VOID *CapsuleImageBase;
+ UINTN CapsuleIndex;
+ UINT8 *FreeMemBase;
+ UINT8 *DestPtr;
+ UINTN DestLength;
+ UINT8 *RelocPtr;
+ UINTN CapsuleTimes;
+ UINT64 SizeLeft;
+ UINT64 CapsuleImageSize;
+ UINTN CapsuleSize;
+ UINTN CapsuleNumber;
+ UINTN DescriptorsSize;
+ UINTN FreeMemSize;
+ UINTN NumDescriptors;
+ BOOLEAN CapsuleBeginFlag;
+ EFI_STATUS Status;
+ EFI_CAPSULE_HEADER *CapsuleHeader;
+ EFI_CAPSULE_PEIM_PRIVATE_DATA PrivateData;
+ EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateDataPtr;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrentBlockDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc;
+ EFI_CAPSULE_BLOCK_DESCRIPTOR PrivateDataDesc[2];
+
+ DEBUG ((EFI_D_INFO, "CapsuleDataCoalesce enter\n"));
+
+ CapsuleIndex = 0;
+ SizeLeft = 0;
+ CapsuleTimes = 0;
+ CapsuleImageSize = 0;
+ PrivateDataPtr = NULL;
+ CapsuleHeader = NULL;
+ CapsuleBeginFlag = TRUE;
+ CapsuleSize = 0;
+ NumDescriptors = 0;
+
+ //
+ // Build capsule descriptors list
+ //
+ Status = BuildCapsuleDescriptors (BlockListBuffer, &BlockList);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DEBUG_CODE (
+ CapsuleTestPatternPreCoalesce (PeiServices, BlockList);
+ );
+
+ //
+ // Get the size of our descriptors and the capsule size. GetCapsuleInfo()
+ // returns the number of descriptors that actually point to data, so add
+ // one for a terminator. Do that below.
+ //
+ Status = GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize, &CapsuleNumber);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ DEBUG ((EFI_D_INFO, "CapsuleSize - 0x%x\n", CapsuleSize));
+ DEBUG ((EFI_D_INFO, "CapsuleNumber - 0x%x\n", CapsuleNumber));
+ DEBUG ((EFI_D_INFO, "NumDescriptors - 0x%x\n", NumDescriptors));
+ if ((CapsuleSize == 0) || (NumDescriptors == 0) || (CapsuleNumber == 0)) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (CapsuleNumber - 1 >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(UINT64))) / sizeof(UINT64)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleNumber - 0x%x\n", CapsuleNumber));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Initialize our local copy of private data. When we're done, we'll create a
+ // descriptor for it as well so that it can be put into free memory without
+ // trashing anything.
+ //
+ PrivateData.Signature = EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE;
+ PrivateData.CapsuleAllImageSize = (UINT64) CapsuleSize;
+ PrivateData.CapsuleNumber = (UINT64) CapsuleNumber;
+ PrivateData.CapsuleOffset[0] = 0;
+ //
+ // NOTE: Only data in sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) is valid, CapsuleOffset field is unitialized at this moment.
+ // The code sets partial length here for Descriptor.Length check, but later it will use full length to reserve those PrivateData region.
+ //
+ PrivateDataDesc[0].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) &PrivateData;
+ PrivateDataDesc[0].Length = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA);
+ PrivateDataDesc[1].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) BlockList;
+ PrivateDataDesc[1].Length = 0;
+ //
+ // Add PrivateDataDesc[0] in beginning beginning, as it is new descriptor. PrivateDataDesc[1] is NOT needed.
+ // In addition, one NULL terminator is added in the end. See RelocateBlockDescriptors().
+ //
+ NumDescriptors += 2;
+ //
+ // Sandity check
+ //
+ if (CapsuleSize >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64)))) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize - 0x%x\n", CapsuleSize));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ //
+ // Need add sizeof(UINT64) for PrivateData alignment
+ //
+ CapsuleSize += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64);
+ BlockList = PrivateDataDesc;
+ //
+ // Sandity check
+ //
+ if (NumDescriptors >= (MAX_ADDRESS / sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
+ DEBUG ((EFI_D_ERROR, "ERROR: NumDescriptors - 0x%x\n", NumDescriptors));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ DescriptorsSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ //
+ // Sandity check
+ //
+ if (DescriptorsSize >= (MAX_ADDRESS - CapsuleSize)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: DescriptorsSize - 0x%lx, CapsuleSize - 0x%lx\n", (UINT64)DescriptorsSize, (UINT64)CapsuleSize));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Don't go below some min address. If the base is below it,
+ // then move it up and adjust the size accordingly.
+ //
+ DEBUG ((EFI_D_INFO, "Capsule Memory range from 0x%8X to 0x%8X\n", (UINTN) *MemoryBase, (UINTN)*MemoryBase + *MemorySize));
+ if ((UINTN)*MemoryBase < (UINTN) MIN_COALESCE_ADDR) {
+ if (((UINTN)*MemoryBase + *MemorySize) < (UINTN) MIN_COALESCE_ADDR) {
+ DEBUG ((EFI_D_ERROR, "ERROR: *MemoryBase + *MemorySize - 0x%x\n", (UINTN)*MemoryBase + *MemorySize));
+ return EFI_BUFFER_TOO_SMALL;
+ } else {
+ *MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase);
+ *MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR;
+ }
+ }
+
+ if (*MemorySize <= (CapsuleSize + DescriptorsSize)) {
+ DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize + DescriptorsSize - 0x%x\n", CapsuleSize + DescriptorsSize));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ FreeMemBase = *MemoryBase;
+ FreeMemSize = *MemorySize;
+ DEBUG ((EFI_D_INFO, "Capsule Free Memory from 0x%8X to 0x%8X\n", (UINTN) FreeMemBase, (UINTN) FreeMemBase + FreeMemSize));
+
+ //
+ // Relocate all the block descriptors to low memory to make further
+ // processing easier.
+ //
+ BlockList = RelocateBlockDescriptors (PeiServices, BlockList, NumDescriptors, FreeMemBase, FreeMemSize);
+ if (BlockList == NULL) {
+ //
+ // Not enough room to relocate the descriptors
+ //
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Take the top of memory for the capsule. UINT64 align up.
+ //
+ DestPtr = FreeMemBase + FreeMemSize - CapsuleSize;
+ DestPtr = (UINT8 *) (((UINTN)DestPtr + sizeof (UINT64) - 1) & ~(sizeof (UINT64) - 1));
+ FreeMemBase = (UINT8 *) BlockList + DescriptorsSize;
+ FreeMemSize = (UINTN) DestPtr - (UINTN) FreeMemBase;
+ NewCapsuleBase = (VOID *) DestPtr;
+ CapsuleImageBase = (UINT8 *)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
+
+ PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase;
+
+ //
+ // Move all the blocks to the top (high) of memory.
+ // Relocate all the obstructing blocks. Note that the block descriptors
+ // were coalesced when they were relocated, so we can just ++ the pointer.
+ //
+ CurrentBlockDesc = BlockList;
+ while ((CurrentBlockDesc->Length != 0) || (CurrentBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
+ if (CapsuleTimes == 0) {
+ //
+ // The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.
+ // CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use.
+ //
+ ASSERT (CurrentBlockDesc->Union.DataBlock == (UINT64)(UINTN)&PrivateData);
+ DestLength = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
+ } else {
+ DestLength = (UINTN)CurrentBlockDesc->Length;
+ }
+ //
+ // See if any of the remaining capsule blocks are in the way
+ //
+ TempBlockDesc = CurrentBlockDesc;
+ while (TempBlockDesc->Length != 0) {
+ //
+ // Is this block in the way of where we want to copy the current descriptor to?
+ //
+ if (IsOverlapped (
+ (UINT8 *) DestPtr,
+ (UINTN) DestLength,
+ (UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,
+ (UINTN) TempBlockDesc->Length
+ )) {
+ //
+ // Relocate the block
+ //
+ RelocPtr = FindFreeMem (BlockList, FreeMemBase, FreeMemSize, (UINTN) TempBlockDesc->Length);
+ if (RelocPtr == NULL) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem ((VOID *) RelocPtr, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);
+ DEBUG ((EFI_D_INFO, "Capsule reloc data block from 0x%8X to 0x%8X with size 0x%8X\n",
+ (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) RelocPtr, (UINTN) TempBlockDesc->Length));
+
+ TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocPtr;
+ }
+ //
+ // Next descriptor
+ //
+ TempBlockDesc++;
+ }
+ //
+ // Ok, we made it through. Copy the block.
+ // we just support greping one capsule from the lists of block descs list.
+ //
+ CapsuleTimes ++;
+ //
+ //Skip the first block descriptor that filled with EFI_CAPSULE_PEIM_PRIVATE_DATA
+ //
+ if (CapsuleTimes > 1) {
+ //
+ //For every capsule entry point, check its header to determine whether to relocate it.
+ //If it is invalid, skip it and move on to the next capsule. If it is valid, relocate it.
+ //
+ if (CapsuleBeginFlag) {
+ CapsuleBeginFlag = FALSE;
+ CapsuleHeader = (EFI_CAPSULE_HEADER*)(UINTN)CurrentBlockDesc->Union.DataBlock;
+ SizeLeft = CapsuleHeader->CapsuleImageSize;
+
+ //
+ // No more check here is needed, because IsCapsuleCorrupted() already in ValidateCapsuleIntegrity()
+ //
+ ASSERT (CapsuleIndex < CapsuleNumber);
+
+ //
+ // Relocate this capsule
+ //
+ CapsuleImageSize += SizeLeft;
+ //
+ // Cache the begin offset of this capsule
+ //
+ ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE);
+ ASSERT ((UINTN)DestPtr >= (UINTN)CapsuleImageBase);
+ PrivateDataPtr->CapsuleOffset[CapsuleIndex++] = (UINT64)((UINTN)DestPtr - (UINTN)CapsuleImageBase);
+ }
+
+ //
+ // Below ASSERT is checked in ValidateCapsuleIntegrity()
+ //
+ ASSERT (CurrentBlockDesc->Length <= SizeLeft);
+
+ CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);
+ DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%lX from 0x%lX to 0x%lX with size 0x%lX\n",(UINT64)CapsuleTimes,
+ CurrentBlockDesc->Union.DataBlock, (UINT64)(UINTN)DestPtr, CurrentBlockDesc->Length));
+ DestPtr += CurrentBlockDesc->Length;
+ SizeLeft -= CurrentBlockDesc->Length;
+
+ if (SizeLeft == 0) {
+ //
+ //Here is the end of the current capsule image.
+ //
+ CapsuleBeginFlag = TRUE;
+ }
+ } else {
+ //
+ // The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.
+ // CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use.
+ //
+ ASSERT (CurrentBlockDesc->Length == sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA));
+ ASSERT ((UINTN)DestPtr == (UINTN)NewCapsuleBase);
+ CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);
+ DestPtr += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
+ }
+ //
+ //Walk through the block descriptor list.
+ //
+ CurrentBlockDesc++;
+ }
+ //
+ // We return the base of memory we want reserved, and the size.
+ // The memory peim should handle it appropriately from there.
+ //
+ *MemorySize = (UINTN) CapsuleSize;
+ *MemoryBase = (VOID *) NewCapsuleBase;
+
+ ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE);
+ ASSERT (PrivateDataPtr->CapsuleAllImageSize == CapsuleImageSize);
+ ASSERT (PrivateDataPtr->CapsuleNumber == CapsuleIndex);
+
+ return EFI_SUCCESS;
+}