From da58b0db33a223751779e5311c947becbfdeb639 Mon Sep 17 00:00:00 2001 From: jljusten Date: Tue, 12 Jul 2011 20:35:46 +0000 Subject: MdeModulePkg: Add Capsule PEIM Signed-off-by: jljusten Reviewed-by: mdkinney Reviewed-by: lgao4 git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12011 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Universal/CapsulePei/Capsule.h | 55 + MdeModulePkg/Universal/CapsulePei/CapsulePei.inf | 64 ++ MdeModulePkg/Universal/CapsulePei/UefiCapsule.c | 1319 ++++++++++++++++++++++ 3 files changed, 1438 insertions(+) create mode 100644 MdeModulePkg/Universal/CapsulePei/Capsule.h create mode 100644 MdeModulePkg/Universal/CapsulePei/CapsulePei.inf create mode 100644 MdeModulePkg/Universal/CapsulePei/UefiCapsule.c (limited to 'MdeModulePkg/Universal') diff --git a/MdeModulePkg/Universal/CapsulePei/Capsule.h b/MdeModulePkg/Universal/CapsulePei/Capsule.h new file mode 100644 index 0000000..2a042f2 --- /dev/null +++ b/MdeModulePkg/Universal/CapsulePei/Capsule.h @@ -0,0 +1,55 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +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. + +**/ + +#ifndef _CAPSULE_PEIM_H_ +#define _CAPSULE_PEIM_H_ + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// +// We want to avoid using memory at 0 for coalescing, so set a +// min address. +// +#define MIN_COALESCE_ADDR 0x100000 +#define MAX_SUPPORT_CAPSULE_NUM 50 + +// +// This capsule PEIM puts its private data at the start of the +// coalesced capsule. Here's the structure definition. +// +#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'D') + +typedef struct { + UINT32 Signature; + UINTN CapsuleSize; +} EFI_CAPSULE_PEIM_PRIVATE_DATA; + +#define CAPSULE_TEST_SIGNATURE SIGNATURE_32('T', 'E', 'S', 'T') + +#endif diff --git a/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf b/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf new file mode 100644 index 0000000..c66e772 --- /dev/null +++ b/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf @@ -0,0 +1,64 @@ +## @file +# Component description file for Capsule module. +# +# Capsule update module supports EFI and UEFI. +# +# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CapsulePei + FILE_GUID = C779F6D8-7113-4AA1-9648-EB1633C7D53B + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = CapsuleMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + UefiCapsule.c + Capsule.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + HobLib + BaseMemoryLib + PeiServicesLib + PeimEntryPoint + DebugLib + PeiServicesTablePointerLib + PrintLib + +[Guids] + gEfiCapsuleVendorGuid # ALWAYS_CONSUMED + + +[Ppis] + gEfiPeiReadOnlyVariable2PpiGuid # PPI ALWAYS_CONSUMED + gPeiCapsulePpiGuid # PPI ALWAYS_CONSUMED + + +[Depex] + gEfiPeiReadOnlyVariable2PpiGuid + + diff --git a/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c b/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c new file mode 100644 index 0000000..7c10a3c --- /dev/null +++ b/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c @@ -0,0 +1,1319 @@ +/** @file + Capsule update PEIM for UEFI2.0 + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ +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 "Capsule.h" + +EFI_PHYSICAL_ADDRESS *mBufferAddress; + +/** + 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; +} + +/** + Check the integrity of the capsule descriptors. + + @param BlockList Pointer to the capsule descriptors + + @retval NULL BlockList is not valid. + @retval LastBlockDesc Last one Block in BlockList + +**/ +EFI_CAPSULE_BLOCK_DESCRIPTOR * +ValidateCapsuleIntegrity ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList + ) +{ + EFI_CAPSULE_HEADER *CapsuleHeader; + UINT64 CapsuleSize; + UINT32 CapsuleCount; + EFI_CAPSULE_BLOCK_DESCRIPTOR *Ptr; + + // + // Go through the list to look for inconsistencies. Check for: + // * misaligned block descriptors. + // * The first capsule header guid + // * The first capsule header flag + // * Data + Length < Data (wrap) + CapsuleSize = 0; + CapsuleCount = 0; + Ptr = BlockList; + 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 & 0x07) { + DEBUG ((EFI_D_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; + } else { + // + //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); + if (IsCapsuleCorrupted (CapsuleHeader)) { + return NULL; + } + CapsuleCount ++; + CapsuleSize = CapsuleHeader->CapsuleImageSize; + } else { + if (CapsuleSize >= Ptr->Length) { + CapsuleSize = CapsuleSize - Ptr->Length; + } else { + CapsuleSize = 0; + } + } + // + // Move to next BLOCK descriptor + // + Ptr++; + } + } + + if (CapsuleCount == 0) { + // + // No any capsule is found in BlockList. + // + return NULL; + } + + return Ptr; +} + + +/** + Checks for the presence of capsule descriptors. + Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2... + + @param BlockList Pointer to the capsule descriptors + + @retval EFI_SUCCESS a valid capsule is present + @retval EFI_NOT_FOUND if a valid capsule is not present +**/ +EFI_STATUS +GetCapsuleDescriptors ( + IN OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockList OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN Size; + UINTN Index; + UINTN TempIndex; + UINTN ValidIndex; + BOOLEAN Flag; + CHAR16 CapsuleVarName[30]; + CHAR16 *TempVarName; + EFI_PHYSICAL_ADDRESS CapsuleDataPtr64; + EFI_CAPSULE_BLOCK_DESCRIPTOR *LastBlock; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlock; + EFI_CAPSULE_BLOCK_DESCRIPTOR *HeadBlock; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + + LastBlock = NULL; + HeadBlock = NULL; + TempBlock = NULL; + Index = 0; + TempVarName = NULL; + CapsuleVarName[0] = 0; + ValidIndex = 0; + + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **) &PPIVariableServices + ); + if (Status == EFI_SUCCESS) { + StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + Size = sizeof (CapsuleDataPtr64); + while (1) { + if (Index == 0) { + // + // For the first Capsule Image + // + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + CapsuleVarName, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + (VOID *) &CapsuleDataPtr64 + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Capsule -- capsule variable not set\n")); + return EFI_NOT_FOUND; + } + // + // We have a chicken/egg situation where the memory init code needs to + // know the boot mode prior to initializing memory. For this case, our + // validate function will fail. We can detect if this is the case if blocklist + // pointer is null. In that case, return success since we know that the + // variable is set. + // + if (BlockList == NULL) { + return EFI_SUCCESS; + } + // + // Test integrity of descriptors. + // + LastBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64); + if (LastBlock == NULL) { + return EFI_NOT_FOUND; + } + // + // Return the base of the block descriptors + // + HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64; + } else { + UnicodeValueToString (TempVarName, 0, Index, 0); + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + CapsuleVarName, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + (VOID *) &CapsuleDataPtr64 + ); + if (EFI_ERROR (Status)) { + break; + } + + // + // If this BlockList has been linked before, skip this variable + // + Flag = FALSE; + for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) { + if (mBufferAddress[TempIndex] == CapsuleDataPtr64) { + Flag = TRUE; + break; + } + } + if (Flag) { + Index ++; + continue; + } + + // + // Test integrity of descriptors. + // + TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64); + if (TempBlock == NULL) { + return EFI_NOT_FOUND; + } + // + // Combine the different BlockList into single BlockList. + // + LastBlock->Union.DataBlock = CapsuleDataPtr64; + LastBlock->Length = 0; + LastBlock = TempBlock; + } + + // + // Cache BlockList which has been processed + // + mBufferAddress[ValidIndex++] = CapsuleDataPtr64; + Index ++; + } + } + + if (HeadBlock != NULL) { + *BlockList = HeadBlock; + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; +} + +/** + 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 + NumDescriptors - optional pointer to where to return the number of descriptors + CapsuleSize - optional pointer to where to return the capsule size + @param NumDescriptors Optional pointer to where to return the number of descriptors + @param CapsuleSize Optional pointer to where to return the capsule size + + @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 + ) +{ + UINTN Count; + UINTN Size; + + ASSERT (Desc != NULL); + + Count = 0; + Size = 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 { + Size += (UINTN) Desc->Length; + Count++; + Desc++; + } + } + // + // If no descriptors, then fail + // + if (Count == 0) { + return EFI_NOT_FOUND; + } + + if (NumDescriptors != NULL) { + *NumDescriptors = Count; + } + + if (CapsuleSize != NULL) { + *CapsuleSize = Size; + } + + return EFI_SUCCESS; +} + + +/** + 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; + // + // 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; + if (*TestPtr != CAPSULE_TEST_SIGNATURE) { + 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; + } +} + + +/** + 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 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; +} + +/** + 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 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 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 NumDescriptors; + 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. + // GetCapsuleInfo() returns the number of legit descriptors, so add one for + // a terminator. + // + if (GetCapsuleInfo (BlockList, &NumDescriptors, NULL) != EFI_SUCCESS) { + return NULL; + } + + NumDescriptors++; + 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); + TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer; + + DEBUG ((EFI_D_INFO, "Capsule relocate descriptors from/to/size 0x%X 0x%X 0x%X\n", (UINT32)(UINTN)TempBlockDesc->Union.DataBlock, (UINT32)(UINTN)RelocBuffer, (UINT32)(UINTN)TempBlockDesc->Length)); + } + } + 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; +} + +/** + Capsule PPI service to coalesce a fragmented capsule in memory. + + Memory Map for coalesced capsule: + MemBase + ---->+---------------------------+<-----------+ + MemSize | CapsuleOffset[49] | | + +---------------------------+ | + | ................ | | + +---------------------------+ | + | CapsuleOffset[2] | | + +---------------------------+ | + | CapsuleOffset[1] | | + +---------------------------+ | + | CapsuleOffset[0] | CapsuleSize + +---------------------------+ | + | CapsuleNumber | | + +---------------------------+ | + | | | + | | | + | Capsule Image | | + | | | + | | | + +---------------------------+ | + | PrivateData | | + DestPtr ----> +---------------------------+<-----------+ + | | | + | FreeMem | FreeMemSize + | | | + FreeMemBase --->+---------------------------+<-----------+ + | Terminator | + +---------------------------+ + | BlockDescriptor n | + +---------------------------+ + | ................. | + +---------------------------+ + | BlockDescriptor 1 | + +---------------------------+ + | BlockDescriptor 0 | + +---------------------------+ + | PrivateDataDesc 0 | + MemBase ---->+---------------------------+<----- BlockList + + + @param PeiServices General purpose services available to every PEIM. + @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 can't determine the boot mode + if the boot mode is not flash-update + 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 if there's no capsule, or if we processed the + capsule successfully. +**/ +EFI_STATUS +EFIAPI +CapsuleCoalesce ( + IN EFI_PEI_SERVICES **PeiServices, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ) +{ + VOID *NewCapsuleBase; + VOID *DataPtr; + UINT8 CapsuleIndex; + UINT8 *FreeMemBase; + UINT8 *DestPtr; + UINT8 *RelocPtr; + UINT32 CapsuleOffset[MAX_SUPPORT_CAPSULE_NUM]; + UINT32 *AddDataPtr; + UINT32 CapsuleTimes; + UINT64 SizeLeft; + UINT64 CapsuleImageSize; + UINTN CapsuleSize; + UINTN DescriptorsSize; + UINTN FreeMemSize; + UINTN NumDescriptors; + UINTN Index; + UINTN Size; + UINTN VariableCount; + CHAR16 CapsuleVarName[30]; + CHAR16 *TempVarName; + EFI_PHYSICAL_ADDRESS CapsuleDataPtr64; + BOOLEAN IsCorrupted; + BOOLEAN CapsuleBeginFlag; + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + 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]; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + + CapsuleIndex = 0; + SizeLeft = 0; + CapsuleTimes = 0; + CapsuleImageSize = 0; + PrivateDataPtr = NULL; + AddDataPtr = NULL; + CapsuleHeader = NULL; + CapsuleBeginFlag = TRUE; + IsCorrupted = TRUE; + CapsuleSize = 0; + NumDescriptors = 0; + Index = 0; + VariableCount = 0; + CapsuleVarName[0] = 0; + + // + // Someone should have already ascertained the boot mode. If it's not + // capsule update, then return normally. + // + Status = PeiServicesGetBootMode (&BootMode); + if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) { + return EFI_NOT_FOUND; + } + + // + // User may set the same ScatterGatherList with several different variables, + // so cache all ScatterGatherList for check later. + // + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **) &PPIVariableServices + ); + if (EFI_ERROR (Status)) { + return Status; + } + Size = sizeof (CapsuleDataPtr64); + StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + while (TRUE) { + if (Index > 0) { + UnicodeValueToString (TempVarName, 0, Index, 0); + } + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + CapsuleVarName, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + (VOID *) &CapsuleDataPtr64 + ); + if (EFI_ERROR (Status)) { + // + // There is no capsule variables, quit + // + DEBUG ((EFI_D_ERROR,"Capsule variable Index = %d\n", Index)); + break; + } + VariableCount++; + Index++; + } + + DEBUG ((EFI_D_ERROR,"Capsule variable count = %d\n", VariableCount)); + + Status = PeiServicesAllocatePool ( + VariableCount * sizeof (EFI_PHYSICAL_ADDRESS), + (VOID **)&mBufferAddress + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status)); + return Status; + } + + // + // Find out if we actually have a capsule. + // + Status = GetCapsuleDescriptors (&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. + // + GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize); + if ((CapsuleSize == 0) || (NumDescriptors == 0)) { + return EFI_NOT_FOUND; + } + + // + // 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.CapsuleSize = CapsuleSize; + 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; + // + // In addition to PrivateDataDesc[1:0], one terminator is added + // See below RelocateBlockDescriptors() + // + NumDescriptors += 3; + CapsuleSize += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(CapsuleOffset) + sizeof(UINT32); + BlockList = PrivateDataDesc; + DescriptorsSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + + // + // 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) { + return EFI_BUFFER_TOO_SMALL; + } else { + *MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase); + *MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR; + } + } + + if (*MemorySize <= (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, 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. Naturally align. + // + DestPtr = FreeMemBase + FreeMemSize - CapsuleSize; + DestPtr = (UINT8 *) ((UINTN) DestPtr &~ (UINTN) (sizeof (UINTN) - 1)); + FreeMemBase = (UINT8 *) BlockList + DescriptorsSize; + FreeMemSize = FreeMemSize - DescriptorsSize - CapsuleSize; + NewCapsuleBase = (VOID *) DestPtr; + + // + // 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)) { + // + // 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) CurrentBlockDesc->Length, + (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; + if (!IsCapsuleCorrupted (CapsuleHeader)) { + + if (CapsuleIndex > (MAX_SUPPORT_CAPSULE_NUM - 1)) { + DEBUG ((EFI_D_ERROR, "Capsule number exceeds the max number of %d!\n", MAX_SUPPORT_CAPSULE_NUM)); + return EFI_BUFFER_TOO_SMALL; + } + + // + // Relocate this valid capsule + // + IsCorrupted = FALSE; + CapsuleImageSize += SizeLeft; + CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length); + DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes, + (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length)); + // + // Cache the begin offset of this capsule + // + CapsuleOffset[CapsuleIndex++] = (UINT32) (UINTN) DestPtr - (UINT32)(UINTN)NewCapsuleBase - (UINT32)sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA); + DestPtr += CurrentBlockDesc->Length; + } + // + // If the current block length is greater than or equal to SizeLeft, this is the + // start of the next capsule + // + if (CurrentBlockDesc->Length < SizeLeft) { + SizeLeft -= CurrentBlockDesc->Length; + } else { + // + // Start the next cycle + // + SizeLeft = 0; + IsCorrupted = TRUE; + CapsuleBeginFlag = TRUE; + } + } else { + // + //Go on relocating the current capule image. + // + if (CurrentBlockDesc->Length < SizeLeft) { + if (!IsCorrupted) { + CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length); + DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes, + (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length)); + DestPtr += CurrentBlockDesc->Length; + } + SizeLeft -= CurrentBlockDesc->Length; + } else { + // + //Here is the end of the current capsule image. + // + if (!IsCorrupted) { + CopyMem ((VOID *) DestPtr, (VOID *)(UINTN)(CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length); + DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes, + (UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length)); + DestPtr += CurrentBlockDesc->Length; + } + // + // Start the next cycle + // + SizeLeft = 0; + IsCorrupted = TRUE; + CapsuleBeginFlag = TRUE; + } + } + } else { + // + //The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA. + // + CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length); + DestPtr += CurrentBlockDesc->Length; + } + // + //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) CapsuleImageSize; + *MemoryBase = (VOID *) NewCapsuleBase; + + // + //Append the offsets of mutiply capsules to the continous buffer + // + DataPtr = (VOID*)((UINTN)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (UINTN)CapsuleImageSize); + AddDataPtr = (UINT32*)(((UINTN) DataPtr + sizeof(UINT32) - 1) &~ (UINT32) (sizeof (UINT32) - 1)); + + *AddDataPtr++ = CapsuleIndex; + + CopyMem (AddDataPtr, &CapsuleOffset[0], sizeof (UINT32) * CapsuleIndex); + + PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase; + PrivateDataPtr->CapsuleSize = (UINTN)CapsuleImageSize; + + return Status; +} + +/** + Determine if we're in capsule update boot mode. + + @param PeiServices PEI services table + + @retval EFI_SUCCESS if we have a capsule available + @retval EFI_NOT_FOUND no capsule detected + +**/ +EFI_STATUS +EFIAPI +CheckCapsuleUpdate ( + IN EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + Status = GetCapsuleDescriptors (NULL); + return Status; +} +/** + This function will look at a capsule and determine if it's a test pattern. + If it is, then it will verify it and emit an error message if corruption is detected. + + @param PeiServices Standard pei services pointer + @param CapsuleBase Base address of coalesced capsule, which is preceeded + by private data. Very implementation specific. + + @retval TRUE Capsule image is the test image + @retval FALSE Capsule image is not the test image. + +**/ +BOOLEAN +CapsuleTestPattern ( + IN EFI_PEI_SERVICES **PeiServices, + IN VOID *CapsuleBase + ) +{ + UINT32 *TestPtr; + UINT32 TestCounter; + UINT32 TestSize; + BOOLEAN RetValue; + + RetValue = FALSE; + + // + // Look at the capsule data and determine if it's a test pattern. If it + // is, then test it now. + // + TestPtr = (UINT32 *) CapsuleBase; + if (*TestPtr == CAPSULE_TEST_SIGNATURE) { + RetValue = TRUE; + DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n")); + TestSize = TestPtr[1] / sizeof (UINT32); + // + // Skip over the signature and the size fields in the pattern data header + // + TestPtr += 2; + TestCounter = 0; + while (TestSize > 0) { + if (*TestPtr != TestCounter) { + DEBUG ((EFI_D_INFO, "Capsule test pattern mode FAILED: BaseAddr/FailAddr 0x%X 0x%X\n", (UINT32)(UINTN)(EFI_CAPSULE_PEIM_PRIVATE_DATA *)CapsuleBase, (UINT32)(UINTN)TestPtr)); + return TRUE; + } + + TestPtr++; + TestCounter++; + TestSize--; + } + + DEBUG ((EFI_D_INFO, "Capsule test pattern mode SUCCESS\n")); + } + + return RetValue; +} + +/** + Capsule PPI service that gets called after memory is available. The + capsule coalesce function, which must be called first, returns a base + address and size, which can be anything actually. Once the memory init + PEIM has discovered memory, then it should call this function and pass in + the base address and size returned by the coalesce function. Then this + function can create a capsule HOB and return. + + @param PeiServices standard pei services pointer + @param CapsuleBase address returned by the capsule coalesce function. Most + likely this will actually be a pointer to private data. + @param CapsuleSize value returned by the capsule coalesce function. + + @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a + coalesced capsule + @retval EFI_SUCCESS if all goes well. +**/ +EFI_STATUS +EFIAPI +CreateState ( + IN EFI_PEI_SERVICES **PeiServices, + IN VOID *CapsuleBase, + IN UINTN CapsuleSize + ) +{ + EFI_STATUS Status; + EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateData; + UINTN Size; + EFI_PHYSICAL_ADDRESS NewBuffer; + UINT32 *DataPtr; + UINT32 CapsuleNumber; + UINT32 Index; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT64 Length; + + DataPtr = NULL; + CapsuleNumber = 0; + PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase; + if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) { + return EFI_VOLUME_CORRUPTED; + } + // + // Capsule Number and Capsule Offset is in the tail of Capsule data. + // + Size = (UINTN) PrivateData->CapsuleSize; + DataPtr = (UINT32*)((UINTN)CapsuleBase + (UINTN)sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA)+ Size); + DataPtr = (UINT32*)(((UINTN) DataPtr + sizeof(UINT32) - 1) & ~(sizeof (UINT32) - 1)); + CapsuleNumber = *DataPtr++; + // + // Allocate the memory so that it gets preserved into DXE + // + Status = PeiServicesAllocatePages ( + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES (Size), + &NewBuffer + ); + + if (Status != EFI_SUCCESS) { + DEBUG ((EFI_D_ERROR, "AllocatePages Failed!\n")); + return Status; + } + // + // Copy to our new buffer for DXE + // + DEBUG ((EFI_D_INFO, "Capsule copy from 0x%8X to 0x%8X with size 0x%8X\n", (UINTN) (PrivateData + 1), (UINTN) NewBuffer, Size)); + CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) (PrivateData + 1), Size); + // + // Check for test data pattern. If it is the test pattern, then we'll + // test it ans still create the HOB so that it can be used to verify + // that capsules don't get corrupted all the way into BDS. BDS will + // still try to turn it into a firmware volume, but will think it's + // corrupted so nothing will happen. + // + DEBUG_CODE ( + CapsuleTestPattern (PeiServices, (VOID *) (UINTN) NewBuffer); + ); + + // + // Build the UEFI Capsule Hob for each capsule image. + // + for (Index = 0; Index < CapsuleNumber; Index ++) { + BaseAddress = NewBuffer + DataPtr[Index]; + Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize; + + BuildCvHob (BaseAddress, Length); + } + + return EFI_SUCCESS; +} + +CONST PEI_CAPSULE_PPI mCapsulePpi = { + CapsuleCoalesce, + CheckCapsuleUpdate, + CreateState +}; + +CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gPeiCapsulePpiGuid, + (PEI_CAPSULE_PPI *) &mCapsulePpi +}; + +/** + Entry point function for the PEIM + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @return EFI_SUCCESS If we installed our PPI + +**/ +EFI_STATUS +EFIAPI +CapsuleMain ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + // + // Just produce our PPI + // + return PeiServicesInstallPpi (&mUefiPpiListCapsule); +} -- cgit v1.1