REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the MdeModulePkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			1345 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1345 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #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
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Validate capsule by MemoryResource.
 | |
| 
 | |
|   @param MemoryResource  Pointer to the buffer of memory resource descriptor.
 | |
|   @param Address         Address to be validated.
 | |
|   @param Size            Size to be validated.
 | |
| 
 | |
|   @retval TRUE  No memory resource descriptor reported in HOB list before capsule Coalesce,
 | |
|                 or it is valid in one MemoryResource.
 | |
|           FALSE It is not in any MemoryResource.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| ValidateCapsuleByMemoryResource (
 | |
|   IN MEMORY_RESOURCE_DESCRIPTOR  *MemoryResource,
 | |
|   IN EFI_PHYSICAL_ADDRESS        Address,
 | |
|   IN UINT64                      Size
 | |
|   )
 | |
| {
 | |
|   UINTN  Index;
 | |
| 
 | |
|   //
 | |
|   // Sanity Check
 | |
|   //
 | |
|   if (Size > MAX_ADDRESS) {
 | |
|     DEBUG ((DEBUG_ERROR, "ERROR: Size(0x%lx) > MAX_ADDRESS\n", Size));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Sanity Check
 | |
|   //
 | |
|   if (Address > (MAX_ADDRESS - Size)) {
 | |
|     DEBUG ((DEBUG_ERROR, "ERROR: Address(0x%lx) > (MAX_ADDRESS - Size(0x%lx))\n", Address, Size));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   if (MemoryResource == NULL) {
 | |
|     //
 | |
|     // No memory resource descriptor reported in HOB list before capsule Coalesce.
 | |
|     //
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   for (Index = 0; MemoryResource[Index].ResourceLength != 0; Index++) {
 | |
|     if ((Address >= MemoryResource[Index].PhysicalStart) &&
 | |
|         ((Address + Size) <= (MemoryResource[Index].PhysicalStart + MemoryResource[Index].ResourceLength)))
 | |
|     {
 | |
|       DEBUG ((
 | |
|         DEBUG_INFO,
 | |
|         "Address(0x%lx) Size(0x%lx) in MemoryResource[0x%x] - Start(0x%lx) Length(0x%lx)\n",
 | |
|         Address,
 | |
|         Size,
 | |
|         Index,
 | |
|         MemoryResource[Index].PhysicalStart,
 | |
|         MemoryResource[Index].ResourceLength
 | |
|         ));
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_ERROR, "ERROR: Address(0x%lx) Size(0x%lx) not in any MemoryResource\n", Address, Size));
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check the integrity of the capsule descriptors.
 | |
| 
 | |
|   @param BlockList       Pointer to the capsule descriptors
 | |
|   @param MemoryResource  Pointer to the buffer of memory resource descriptor.
 | |
| 
 | |
|   @retval NULL           BlockList is not valid.
 | |
|   @retval LastBlockDesc  Last one Block in BlockList
 | |
| 
 | |
| **/
 | |
| EFI_CAPSULE_BLOCK_DESCRIPTOR *
 | |
| ValidateCapsuleIntegrity (
 | |
|   IN EFI_CAPSULE_BLOCK_DESCRIPTOR  *BlockList,
 | |
|   IN MEMORY_RESOURCE_DESCRIPTOR    *MemoryResource
 | |
|   )
 | |
| {
 | |
|   EFI_CAPSULE_HEADER            *CapsuleHeader;
 | |
|   UINT64                        CapsuleSize;
 | |
|   UINTN                         CapsuleCount;
 | |
|   EFI_CAPSULE_BLOCK_DESCRIPTOR  *Ptr;
 | |
| 
 | |
|   DEBUG ((DEBUG_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
 | |
|   //   * Below check will be done in ValidateCapsuleByMemoryResource()
 | |
|   //     Length > MAX_ADDRESS
 | |
|   //     Ptr + sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR) > MAX_ADDRESS
 | |
|   //     DataBlock + Length > MAX_ADDRESS
 | |
|   //
 | |
|   CapsuleSize  = 0;
 | |
|   CapsuleCount = 0;
 | |
|   Ptr          = BlockList;
 | |
| 
 | |
|   if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS)(UINTN)Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "Ptr - 0x%p\n", Ptr));
 | |
|   DEBUG ((DEBUG_INFO, "Ptr->Length - 0x%lx\n", Ptr->Length));
 | |
|   DEBUG ((DEBUG_INFO, "Ptr->Union - 0x%lx\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 ((DEBUG_ERROR, "ERROR: BlockList address failed alignment check\n"));
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|     if (Ptr->Length == 0) {
 | |
|       //
 | |
|       // Descriptor points to another list of block descriptors somewhere
 | |
|       // else.
 | |
|       //
 | |
|       Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR  *)(UINTN)Ptr->Union.ContinuationPointer;
 | |
|       if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS)(UINTN)Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
 | |
|         return NULL;
 | |
|       }
 | |
| 
 | |
|       DEBUG ((DEBUG_INFO, "Ptr(C) - 0x%p\n", Ptr));
 | |
|       DEBUG ((DEBUG_INFO, "Ptr->Length - 0x%lx\n", Ptr->Length));
 | |
|       DEBUG ((DEBUG_INFO, "Ptr->Union - 0x%lx\n", Ptr->Union.ContinuationPointer));
 | |
|     } else {
 | |
|       if (!ValidateCapsuleByMemoryResource (MemoryResource, 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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, "ERROR: CapsuleSize(0x%lx) < Ptr->Length(0x%lx)\n", CapsuleSize, Ptr->Length));
 | |
|         //
 | |
|         // Sanity check
 | |
|         //
 | |
|         return NULL;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Move to next BLOCK descriptor
 | |
|       //
 | |
|       Ptr++;
 | |
|       if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS)(UINTN)Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
 | |
|         return NULL;
 | |
|       }
 | |
| 
 | |
|       DEBUG ((DEBUG_INFO, "Ptr(B) - 0x%p\n", Ptr));
 | |
|       DEBUG ((DEBUG_INFO, "Ptr->Length - 0x%lx\n", Ptr->Length));
 | |
|       DEBUG ((DEBUG_INFO, "Ptr->Union - 0x%lx\n", Ptr->Union.ContinuationPointer));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (CapsuleCount == 0) {
 | |
|     //
 | |
|     // No any capsule is found in BlockList
 | |
|     //
 | |
|     DEBUG ((DEBUG_ERROR, "ERROR: CapsuleCount(0x%x) == 0\n", CapsuleCount));
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   if (CapsuleSize != 0) {
 | |
|     //
 | |
|     // Capsule data is incomplete.
 | |
|     //
 | |
|     DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_INFO, "Capsule test pattern pre-coalesce punted #2\n"));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     while (TestSize > 0) {
 | |
|       if (*TestPtr != TestCounter) {
 | |
|         DEBUG ((DEBUG_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 MemoryResource             Pointer to the buffer of memory resource descriptor.
 | |
|   @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,
 | |
|   IN  MEMORY_RESOURCE_DESCRIPTOR    *MemoryResource,
 | |
|   OUT EFI_CAPSULE_BLOCK_DESCRIPTOR  **BlockDescriptorList
 | |
|   )
 | |
| {
 | |
|   UINTN                         Index;
 | |
|   EFI_CAPSULE_BLOCK_DESCRIPTOR  *LastBlock;
 | |
|   EFI_CAPSULE_BLOCK_DESCRIPTOR  *TempBlock;
 | |
|   EFI_CAPSULE_BLOCK_DESCRIPTOR  *HeadBlock;
 | |
| 
 | |
|   DEBUG ((DEBUG_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], MemoryResource);
 | |
|       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 ((DEBUG_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    Pointer to the buffer of Capsule Descriptor Variables.
 | |
|   @param MemoryResource     Pointer to the buffer of memory resource descriptor.
 | |
|   @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 MEMORY_RESOURCE_DESCRIPTOR  *MemoryResource,
 | |
|   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 ((DEBUG_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, MemoryResource, &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 ((DEBUG_INFO, "CapsuleSize - 0x%x\n", CapsuleSize));
 | |
|   DEBUG ((DEBUG_INFO, "CapsuleNumber - 0x%x\n", CapsuleNumber));
 | |
|   DEBUG ((DEBUG_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 ((DEBUG_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 uninitialized 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, as it is new descriptor. PrivateDataDesc[1] is NOT needed.
 | |
|   // In addition, one NULL terminator is added in the end. See RelocateBlockDescriptors().
 | |
|   //
 | |
|   NumDescriptors += 2;
 | |
|   //
 | |
|   // Sanity check
 | |
|   //
 | |
|   if (CapsuleSize >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof (UINT64) + sizeof (UINT64)))) {
 | |
|     DEBUG ((DEBUG_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;
 | |
|   //
 | |
|   // Sanity check
 | |
|   //
 | |
|   if (NumDescriptors >= (MAX_ADDRESS / sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
 | |
|     DEBUG ((DEBUG_ERROR, "ERROR: NumDescriptors - 0x%x\n", NumDescriptors));
 | |
|     return EFI_BUFFER_TOO_SMALL;
 | |
|   }
 | |
| 
 | |
|   DescriptorsSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
 | |
|   //
 | |
|   // Sanity check
 | |
|   //
 | |
|   if (DescriptorsSize >= (MAX_ADDRESS - CapsuleSize)) {
 | |
|     DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_ERROR, "ERROR: CapsuleSize + DescriptorsSize - 0x%x\n", CapsuleSize + DescriptorsSize));
 | |
|     return EFI_BUFFER_TOO_SMALL;
 | |
|   }
 | |
| 
 | |
|   FreeMemBase = *MemoryBase;
 | |
|   FreeMemSize = *MemorySize;
 | |
|   DEBUG ((DEBUG_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 ((
 | |
|           DEBUG_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++] = (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 ((
 | |
|         DEBUG_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;
 | |
| }
 |