| /** @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 - 2017, 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 | |
| ); | |
| /** | |
| 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 ((EFI_D_ERROR, "ERROR: Size(0x%lx) > MAX_ADDRESS\n", Size)); | |
| return FALSE; | |
| } | |
| // | |
| // Sanity Check | |
| // | |
| if (Address > (MAX_ADDRESS - Size)) { | |
| DEBUG ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((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 | |
| // * 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 ((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; | |
| } | |
| 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 ((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 { | |
| 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 ((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++; | |
| if (!ValidateCapsuleByMemoryResource (MemoryResource, (EFI_PHYSICAL_ADDRESS) (UINTN) Ptr, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR))) { | |
| return NULL; | |
| } | |
| 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 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 ((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], 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 ((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 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 ((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, 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 ((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 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 ((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; | |
| // | |
| // Sanity 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); | |
| // | |
| // Sanity 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++] = (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; | |
| } |