From ab7017fe2bf2c1944090d347e7853e087d8641e8 Mon Sep 17 00:00:00 2001 From: li-elvin Date: Fri, 2 Sep 2011 11:34:35 +0000 Subject: Add capsule > 4GB support. When capsule data is put above 4GB, IA32 PEI transfers to long mode to get capsule data. Signed-off-by: li-elvin Reviewed-by: lgao4, mdkinney git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12264 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Universal/CapsulePei/Capsule.h | 72 +- MdeModulePkg/Universal/CapsulePei/CapsulePei.inf | 11 +- MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf | 48 + .../Universal/CapsulePei/Common/CapsuleCoalesce.c | 1104 ++++++++++++++++ .../Universal/CapsulePei/Common/CommonHeader.h | 84 ++ MdeModulePkg/Universal/CapsulePei/UefiCapsule.c | 1360 ++++++++------------ MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c | 66 + 7 files changed, 1899 insertions(+), 846 deletions(-) create mode 100644 MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf create mode 100644 MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c create mode 100644 MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h create mode 100644 MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c (limited to 'MdeModulePkg/Universal/CapsulePei') diff --git a/MdeModulePkg/Universal/CapsulePei/Capsule.h b/MdeModulePkg/Universal/CapsulePei/Capsule.h index 2a042f288d..8d8658a0c1 100644 --- a/MdeModulePkg/Universal/CapsulePei/Capsule.h +++ b/MdeModulePkg/Universal/CapsulePei/Capsule.h @@ -20,7 +20,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include - +#include #include #include @@ -31,25 +31,71 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include +#include +#include +#include +#include +#include "Common/CommonHeader.h" + +#pragma pack(1) // -// We want to avoid using memory at 0 for coalescing, so set a -// min address. +// Page-Map Level-4 Offset (PML4) and +// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB // -#define MIN_COALESCE_ADDR 0x100000 -#define MAX_SUPPORT_CAPSULE_NUM 50 + +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Reserved:1; // Reserved + UINT64 MustBeZero:2; // Must Be Zero + UINT64 Available:3; // Available for use by system software + UINT64 PageTableBaseAddress:40; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // No Execute bit + } Bits; + UINT64 Uint64; +} PAGE_MAP_AND_DIRECTORY_POINTER; // -// This capsule PEIM puts its private data at the start of the -// coalesced capsule. Here's the structure definition. +// Page Table Entry 2MB // -#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'D') +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 MustBe1:1; // Must be 1 + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PAT:1; // + UINT64 MustBeZero:8; // Must be zero; + UINT64 PageTableBaseAddress:31; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_ENTRY; -typedef struct { - UINT32 Signature; - UINTN CapsuleSize; -} EFI_CAPSULE_PEIM_PRIVATE_DATA; +#pragma pack() -#define CAPSULE_TEST_SIGNATURE SIGNATURE_32('T', 'E', 'S', 'T') +typedef +EFI_STATUS +(*COALESCE_ENTRY) ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ); #endif diff --git a/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf b/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf index c66e7720ca..2599cdbcd1 100644 --- a/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf +++ b/MdeModulePkg/Universal/CapsulePei/CapsulePei.inf @@ -3,7 +3,7 @@ # # Capsule update module supports EFI and UEFI. # -# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+# 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 @@ -34,6 +34,7 @@ [Sources] UefiCapsule.c Capsule.h + Common/CapsuleCoalesce.c [Packages] MdePkg/MdePkg.dec @@ -48,6 +49,10 @@ DebugLib PeiServicesTablePointerLib PrintLib + PeCoffLib + PeCoffGetEntryPointLib + PcdLib + ReportStatusCodeLib [Guids] gEfiCapsuleVendorGuid # ALWAYS_CONSUMED @@ -56,7 +61,11 @@ [Ppis] gEfiPeiReadOnlyVariable2PpiGuid # PPI ALWAYS_CONSUMED gPeiCapsulePpiGuid # PPI ALWAYS_CONSUMED + gEfiPeiLoadFilePpiGuid # PPI ALWAYS_CONSUMED +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleCoalesceFile + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode [Depex] gEfiPeiReadOnlyVariable2PpiGuid diff --git a/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf b/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf new file mode 100644 index 0000000000..b12d3e57e4 --- /dev/null +++ b/MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf @@ -0,0 +1,48 @@ +## @file +# Component description file for CapsuleX64 module. +# +# The X64 entrypoint to process capsule in long mode. +# This module is built as X64. +# +# Copyright (c) 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CapsuleX64 + FILE_GUID = F7FDE4A6-294C-493c-B50F-9734553BB757 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + X64/X64Entry.c + Common/CapsuleCoalesce.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + +[Depex] + FALSE + + diff --git a/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c b/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c new file mode 100644 index 0000000000..76c719a54e --- /dev/null +++ b/MdeModulePkg/Universal/CapsulePei/Common/CapsuleCoalesce.c @@ -0,0 +1,1104 @@ +/** @file + The logic to process capsule. + +Copyright (c) 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 +#include + +#include + +#include +#include +#include +#include + +#define MIN_COALESCE_ADDR (1024 * 1024) +#define MAX_SUPPORT_CAPSULE_NUM 50 + +#define EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'a', 'p', 'D') + +typedef struct { + UINT32 Signature; + UINT32 CapsuleSize; +} EFI_CAPSULE_PEIM_PRIVATE_DATA; + +/** + Given a pointer to the capsule block list, info on the available system + memory, and the size of a buffer, find a free block of memory where a + buffer of the given size can be copied to safely. + + @param BlockList Pointer to head of capsule block descriptors + @param MemBase Pointer to the base of memory in which we want to find free space + @param MemSize The size of the block of memory pointed to by MemBase + @param DataSize How big a free block we want to find + + @return A pointer to a memory block of at least DataSize that lies somewhere + between MemBase and (MemBase + MemSize). The memory pointed to does not + contain any of the capsule block descriptors or capsule blocks pointed to + by the BlockList. + +**/ +UINT8 * +FindFreeMem ( + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + UINT8 *MemBase, + UINTN MemSize, + UINTN DataSize + ); + +/** + Check the integrity of the capsule descriptors. + + @param BlockList Pointer to the capsule descriptors + + @retval NULL BlockList is not valid. + @retval LastBlockDesc Last one Block in BlockList + +**/ +EFI_CAPSULE_BLOCK_DESCRIPTOR * +ValidateCapsuleIntegrity ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList + ); + +/** + The capsule block descriptors may be fragmented and spread all over memory. + To simplify the coalescing of capsule blocks, first coalesce all the + capsule block descriptors low in memory. + + The descriptors passed in can be fragmented throughout memory. Here + they are relocated into memory to turn them into a contiguous (null + terminated) array. + + @param PeiServices pointer to PEI services table + @param BlockList pointer to the capsule block descriptors + @param 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 + ); + +/** + 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 + 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 + ); + +/** + Given a pointer to the capsule block list, info on the available system + memory, and the size of a buffer, find a free block of memory where a + buffer of the given size can be copied to safely. + + @param BlockList Pointer to head of capsule block descriptors + @param MemBase Pointer to the base of memory in which we want to find free space + @param MemSize The size of the block of memory pointed to by MemBase + @param DataSize How big a free block we want to find + + @return A pointer to a memory block of at least DataSize that lies somewhere + between MemBase and (MemBase + MemSize). The memory pointed to does not + contain any of the capsule block descriptors or capsule blocks pointed to + by the BlockList. + +**/ +UINT8 * +FindFreeMem ( + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList, + UINT8 *MemBase, + UINTN MemSize, + UINTN DataSize + ) +{ + UINTN Size; + EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrDesc; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempDesc; + UINT8 *MemEnd; + BOOLEAN Failed; + + // + // Need at least enough to copy the data to at the end of the buffer, so + // say the end is less the data size for easy comparisons here. + // + MemEnd = MemBase + MemSize - DataSize; + CurrDesc = BlockList; + // + // Go through all the descriptor blocks and see if any obstruct the range + // + while (CurrDesc != NULL) { + // + // Get the size of this block list and see if it's in the way + // + Failed = FALSE; + TempDesc = CurrDesc; + Size = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + while (TempDesc->Length != 0) { + Size += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + TempDesc++; + } + + if (IsOverlapped (MemBase, DataSize, (UINT8 *) CurrDesc, Size)) { + // + // Set our new base to the end of this block list and start all over + // + MemBase = (UINT8 *) CurrDesc + Size; + CurrDesc = BlockList; + if (MemBase > MemEnd) { + return NULL; + } + + Failed = TRUE; + } + // + // Now go through all the blocks and make sure none are in the way + // + while ((CurrDesc->Length != 0) && (!Failed)) { + if (IsOverlapped (MemBase, DataSize, (UINT8 *) (UINTN) CurrDesc->Union.DataBlock, (UINTN) CurrDesc->Length)) { + // + // Set our new base to the end of this block and start all over + // + Failed = TRUE; + MemBase = (UINT8 *) ((UINTN) CurrDesc->Union.DataBlock) + CurrDesc->Length; + CurrDesc = BlockList; + if (MemBase > MemEnd) { + return NULL; + } + } + CurrDesc++; + } + // + // Normal continuation -- jump to next block descriptor list + // + if (!Failed) { + CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) CurrDesc->Union.ContinuationPointer; + } + } + return MemBase; +} + +/** + Check the integrity of the capsule descriptors. + + @param BlockList Pointer to the capsule descriptors + + @retval NULL BlockList is not valid. + @retval LastBlockDesc Last one Block in BlockList + +**/ +EFI_CAPSULE_BLOCK_DESCRIPTOR * +ValidateCapsuleIntegrity ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList + ) +{ + EFI_CAPSULE_HEADER *CapsuleHeader; + UINT64 CapsuleSize; + 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; +} + +/** + 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; +} + +/** + 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 + 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; + + 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; +} + +/** + 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; + // + // Find first data descriptor + // + while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer; + } + + if (Desc->Union.ContinuationPointer == 0) { + return ; + } + // + // First one better be long enough to at least hold the test signature + // + if (Desc->Length < sizeof (UINT32)) { + DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #1\n")); + return ; + } + + TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock; + // + // 0x54534554 "TEST" + // + if (*TestPtr != 0x54534554) { + return ; + } + + TestCounter = 0; + TestSize = (UINT32) Desc->Length - 2 * sizeof (UINT32); + // + // Skip over the signature and the size fields in the pattern data header + // + TestPtr += 2; + while (1) { + if ((TestSize & 0x03) != 0) { + DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #2\n")); + return ; + } + + while (TestSize > 0) { + if (*TestPtr != TestCounter) { + DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce failed data corruption check\n")); + return ; + } + + TestSize -= sizeof (UINT32); + TestCounter++; + TestPtr++; + } + Desc++; + while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) { + Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer; + } + + if (Desc->Union.ContinuationPointer == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) { + return ; + } + TestSize = (UINT32) Desc->Length; + TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock; + } +} + +/** + Checks for the presence of capsule descriptors. + Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2... + + @param BlockListBuffer Pointer to the buffer of capsule descriptors variables + @param BlockDescriptorList Pointer to the capsule descriptors list + + @retval EFI_SUCCESS a valid capsule is present + @retval EFI_NOT_FOUND if a valid capsule is not present +**/ +EFI_STATUS +BuildCapsuleDescriptors ( + IN EFI_PHYSICAL_ADDRESS *BlockListBuffer, + OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptorList + ) +{ + UINTN Index; + EFI_CAPSULE_BLOCK_DESCRIPTOR *LastBlock; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlock; + EFI_CAPSULE_BLOCK_DESCRIPTOR *HeadBlock; + + LastBlock = NULL; + HeadBlock = NULL; + TempBlock = NULL; + Index = 0; + + while (BlockListBuffer[Index] != 0) { + if (Index == 0) { + // + // For the first Capsule Image, test integrity of descriptors. + // + LastBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]); + if (LastBlock == NULL) { + return EFI_NOT_FOUND; + } + // + // Return the base of the block descriptors + // + HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]; + } else { + // + // Test integrity of descriptors. + // + TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]); + if (TempBlock == NULL) { + return EFI_NOT_FOUND; + } + // + // Combine the different BlockList into single BlockList. + // + LastBlock->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)BlockListBuffer[Index]; + LastBlock->Length = 0; + LastBlock = TempBlock; + } + 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 | 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 BlockListBuffer Point to the buffer of Capsule Descriptor Variables. + @param MemoryBase Pointer to the base of a block of memory that we can walk + all over while trying to coalesce our buffers. + On output, this variable will hold the base address of + a coalesced capsule. + @param MemorySize Size of the memory region pointed to by MemoryBase. + On output, this variable will contain the size of the + coalesced capsule. + + @retval EFI_NOT_FOUND If we could not find the capsule descriptors. + + @retval EFI_BUFFER_TOO_SMALL + If we could not coalesce the capsule in the memory + region provided to us. + + @retval EFI_SUCCESS Processed the capsule successfully. +**/ +EFI_STATUS +EFIAPI +CapsuleDataCoalesce ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PHYSICAL_ADDRESS *BlockListBuffer, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ) +{ + VOID *NewCapsuleBase; + VOID *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; + BOOLEAN IsCorrupted; + 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]; + + CapsuleIndex = 0; + SizeLeft = 0; + CapsuleTimes = 0; + CapsuleImageSize = 0; + PrivateDataPtr = NULL; + AddDataPtr = NULL; + CapsuleHeader = NULL; + CapsuleBeginFlag = TRUE; + IsCorrupted = TRUE; + CapsuleSize = 0; + NumDescriptors = 0; + + // + // Build capsule descriptors list + // + Status = BuildCapsuleDescriptors (BlockListBuffer, &BlockList); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG_CODE ( + CapsuleTestPatternPreCoalesce (PeiServices, BlockList); + ); + + // + // Get the size of our descriptors and the capsule size. GetCapsuleInfo() + // returns the number of descriptors that actually point to data, so add + // one for a terminator. Do that below. + // + 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 = (UINT32) 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%8lX to 0x%8lX 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%8lX to 0x%8lX 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%8lX to 0x%8lX 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 = (UINT32) CapsuleImageSize; + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h b/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h new file mode 100644 index 0000000000..5c0aa3068c --- /dev/null +++ b/MdeModulePkg/Universal/CapsulePei/Common/CommonHeader.h @@ -0,0 +1,84 @@ +/** @file + Common header file. + +Copyright (c) 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_THUNK_32_TO_64_ +#define _CAPSULE_THUNK_32_TO_64_ + +#include +#include "PiPei.h" + +// +// 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') + +typedef struct { + EFI_PHYSICAL_ADDRESS EntryPoint; + EFI_PHYSICAL_ADDRESS StackBufferBase; + UINT64 StackBufferLength; + EFI_PHYSICAL_ADDRESS JumpBuffer; + EFI_PHYSICAL_ADDRESS BlockListAddr; + EFI_PHYSICAL_ADDRESS MemoryBase64Ptr; + EFI_PHYSICAL_ADDRESS MemorySize64Ptr; +} SWITCH_32_TO_64_CONTEXT; + +typedef struct { + UINT16 ReturnCs; + EFI_PHYSICAL_ADDRESS ReturnEntryPoint; + UINT64 ReturnStatus; + IA32_DESCRIPTOR Gdtr; +} SWITCH_64_TO_32_CONTEXT; + +/** + The function to coalesce a fragmented capsule in memory. + + @param PeiServices General purpose services available to every PEIM. + @param BlockListBuffer Point to the buffer of Capsule Descriptor Variables. + @param MemoryBase Pointer to the base of a block of memory that we can walk + all over while trying to coalesce our buffers. + On output, this variable will hold the base address of + a coalesced capsule. + @param MemorySize Size of the memory region pointed to by MemoryBase. + On output, this variable will contain the size of the + coalesced capsule. + + @retval EFI_NOT_FOUND if we 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 +CapsuleDataCoalesce ( + IN EFI_PEI_SERVICES **PeiServices, + IN IN EFI_PHYSICAL_ADDRESS *BlockListBuffer, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ); + +#endif diff --git a/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c b/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c index 7c10a3c568..5598755d46 100644 --- a/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c +++ b/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c @@ -16,136 +16,420 @@ 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 +// +// Global Descriptor Table (GDT) +// +GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries[] = { +/* selector { Global Segment Descriptor } */ +/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor +/* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor +/* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor +/* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor +/* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor +/* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor +/* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor +/* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor +/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor +}; - @retval FALSE Capsule is OK - @retval TRUE Capsule is corrupted +// +// IA32 Gdt register +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = { + sizeof (mGdtEntries) - 1, + (UINTN) mGdtEntries + }; +/** + Calculate the total size of page table. + + @return The size of page table. + + **/ -BOOLEAN -IsCapsuleCorrupted ( - IN EFI_CAPSULE_HEADER *CapsuleHeader +UINTN +CalculatePageTableSize ( + VOID ) { + UINTN TotalPagesNum; + UINT8 PhysicalAddressBits; + VOID *Hob; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + // - //A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET. + // Get physical address bits supported from CPU HOB. // - if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) { - return TRUE; + PhysicalAddressBits = 36; + + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; } + // - //Make sure the flags combination is supported by the platform. + // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. // - if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) { - return TRUE; + ASSERT (PhysicalAddressBits <= 52); + if (PhysicalAddressBits > 48) { + PhysicalAddressBits = 48; } - if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) { - return TRUE; + + // + // Calculate the table entries needed. + // + if (PhysicalAddressBits <= 39 ) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30); + } else { + NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39); + NumberOfPdpEntriesNeeded = 512; } - return FALSE; + TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1; + + return EFI_PAGES_TO_SIZE (TotalPagesNum); } /** - Check the integrity of the capsule descriptors. - - @param BlockList Pointer to the capsule descriptors + Allocates and fills in the Page Directory and Page Table Entries to + establish a 1:1 Virtual to Physical mapping. - @retval NULL BlockList is not valid. - @retval LastBlockDesc Last one Block in BlockList + @param[in] PageTablesAddress The base address of page table. **/ -EFI_CAPSULE_BLOCK_DESCRIPTOR * -ValidateCapsuleIntegrity ( - IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList +VOID +CreateIdentityMappingPageTables ( + IN EFI_PHYSICAL_ADDRESS PageTablesAddress ) -{ - 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)) { +{ + UINT8 PhysicalAddressBits; + EFI_PHYSICAL_ADDRESS PageAddress; + UINTN IndexOfPml4Entries; + UINTN IndexOfPdpEntries; + UINTN IndexOfPageDirectoryEntries; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMap; + PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + UINTN BigPageAddress; + VOID *Hob; + + // + // Get physical address bits supported from CPU HOB. + // + PhysicalAddressBits = 36; + + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + } + + // + // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. + // + ASSERT (PhysicalAddressBits <= 52); + if (PhysicalAddressBits > 48) { + PhysicalAddressBits = 48; + } + + // + // Calculate the table entries needed. + // + if (PhysicalAddressBits <= 39 ) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30); + } else { + NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39); + NumberOfPdpEntriesNeeded = 512; + } + + // + // Pre-allocate big pages to avoid later allocations. + // + BigPageAddress = (UINTN) PageTablesAddress; + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + PageMap = (VOID *) BigPageAddress; + BigPageAddress += EFI_PAGE_SIZE; + + PageMapLevel4Entry = PageMap; + PageAddress = 0; + for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) { // - // Make sure the descriptor is aligned at UINT64 in memory + // Each PML4 entry points to a page of Page Directory Pointer entires. + // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop. // - if ((UINTN) Ptr & 0x07) { - DEBUG ((EFI_D_ERROR, "BlockList address failed alignment check\n")); - return NULL; - } + PageDirectoryPointerEntry = (VOID *) BigPageAddress; + BigPageAddress += EFI_PAGE_SIZE; - if (Ptr->Length == 0) { - // - // Descriptor points to another list of block descriptors somewhere - // else. + // + // Make a PML4 Entry + // + PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry; + PageMapLevel4Entry->Bits.ReadWrite = 1; + PageMapLevel4Entry->Bits.Present = 1; + + for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { // - Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Ptr->Union.ContinuationPointer; - } else { + // Each Directory Pointer entries points to a page of Page Directory entires. + // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop. + // + PageDirectoryEntry = (VOID *) BigPageAddress; + BigPageAddress += EFI_PAGE_SIZE; + // - //To enhance the reliability of check-up, the first capsule's header is checked here. - //More reliabilities check-up will do later. + // Fill in a Page Directory Pointer Entries // - if (CapsuleSize == 0) { + PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry; + PageDirectoryPointerEntry->Bits.ReadWrite = 1; + PageDirectoryPointerEntry->Bits.Present = 1; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += 0x200000) { // - //Move to the first capsule to check its header. + // Fill in the Page Directory entries // - 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; - } + PageDirectoryEntry->Uint64 = (UINT64)PageAddress; + PageDirectoryEntry->Bits.ReadWrite = 1; + PageDirectoryEntry->Bits.Present = 1; + PageDirectoryEntry->Bits.MustBe1 = 1; + } - // - // Move to next BLOCK descriptor - // - Ptr++; } } - if (CapsuleCount == 0) { + // + // For the PML4 entries we are not using fill in a null entry. + // For now we just copy the first entry. + // + for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) { + CopyMem ( + PageMapLevel4Entry, + PageMap, + sizeof (PAGE_MAP_AND_DIRECTORY_POINTER) + ); + } +} + +/** + Return function from long mode to 32-bit mode. + + @param EntrypointContext Context for mode switching + @param ReturnContext Context for mode switching + +**/ +VOID +ReturnFunction ( + SWITCH_32_TO_64_CONTEXT *EntrypointContext, + SWITCH_64_TO_32_CONTEXT *ReturnContext + ) +{ + // + // Restore original GDT + // + AsmWriteGdtr (&ReturnContext->Gdtr); + + // + // return to original caller + // + LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)EntrypointContext->JumpBuffer, 1); + + // + // never be here + // + ASSERT (FALSE); +} + +/** + Thunk function from 32-bit protection mode to long mode. + + @param PageTableAddress Page table base address + @param Context Context for mode switching + @param ReturnContext Context for mode switching + + @retval EFI_SUCCESS Function successfully executed. + +**/ +EFI_STATUS +Thunk32To64 ( + EFI_PHYSICAL_ADDRESS PageTableAddress, + SWITCH_32_TO_64_CONTEXT *Context, + SWITCH_64_TO_32_CONTEXT *ReturnContext + ) +{ + UINTN SetJumpFlag; + EFI_STATUS Status; + + // + // Save return address, LongJump will return here then + // + SetJumpFlag = SetJump ((BASE_LIBRARY_JUMP_BUFFER *) (UINTN) Context->JumpBuffer); + + if (SetJumpFlag == 0) { + // - // No any capsule is found in BlockList. + // Build Page Tables for all physical memory processor supports // - return NULL; - } + CreateIdentityMappingPageTables (PageTableAddress); + + // + // Create 64-bit GDT + // + AsmWriteGdtr (&mGdt); + + // + // Write CR3 + // + AsmWriteCr3 ((UINTN) PageTableAddress); - return Ptr; + // + // Transfer to long mode + // + AsmEnablePaging64 ( + 0x38, + (UINT64) Context->EntryPoint, + (UINT64)(UINTN) Context, + (UINT64)(UINTN) ReturnContext, + Context->StackBufferBase + Context->StackBufferLength + ); + } + + // + // Convert to 32-bit Status and return + // + Status = EFI_SUCCESS; + if ((UINTN) ReturnContext->ReturnStatus != 0) { + Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus); + } + + return Status; } +/** + If in 32 bit protection mode, and coalesce image is of X64, switch to long mode. + + @param LongModeBuffer The context of long mode. + @param CoalesceEntry Entry of coalesce image. + @param BlockListAddr Address of block list. + @param MemoryBase Base of memory range. + @param MemorySize Size of memory range. + + @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce. + @retval Others Failed to execute coalesce in long mode. + +**/ +EFI_STATUS +ModeSwitch ( + IN EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer, + IN COALESCE_ENTRY CoalesceEntry, + IN EFI_PHYSICAL_ADDRESS BlockListAddr, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemorySize + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS MemoryBase64; + UINT64 MemorySize64; + EFI_PHYSICAL_ADDRESS MemoryEnd64; + SWITCH_32_TO_64_CONTEXT Context; + SWITCH_64_TO_32_CONTEXT ReturnContext; + BASE_LIBRARY_JUMP_BUFFER JumpBuffer; + EFI_PHYSICAL_ADDRESS ReservedRangeBase; + EFI_PHYSICAL_ADDRESS ReservedRangeEnd; + + ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT)); + ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT)); + + MemoryBase64 = (UINT64) (UINTN) *MemoryBase; + MemorySize64 = (UINT64) (UINTN) *MemorySize; + MemoryEnd64 = MemoryBase64 + MemorySize64; + + // + // Merge memory range reserved for stack and page table + // + if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) { + ReservedRangeBase = LongModeBuffer->StackBaseAddress; + ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize (); + } else { + ReservedRangeBase = LongModeBuffer->PageTableAddress; + ReservedRangeEnd = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize; + } + + // + // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize. + // If they are overlapped, get a larger range to process capsule data. + // + if (ReservedRangeBase <= MemoryBase64) { + if (ReservedRangeEnd < MemoryEnd64) { + MemoryBase64 = ReservedRangeEnd; + } else { + DEBUG ((EFI_D_ERROR, "Memory is not enough to process capsule!\n")); + return EFI_OUT_OF_RESOURCES; + } + } else if (ReservedRangeBase < MemoryEnd64) { + if (ReservedRangeEnd < MemoryEnd64 && + ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) { + MemoryBase64 = ReservedRangeEnd; + } else { + MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64); + } + } + + // + // Initialize context jumping to 64-bit enviroment + // + Context.JumpBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer; + Context.StackBufferBase = LongModeBuffer->StackBaseAddress; + Context.StackBufferLength = LongModeBuffer->StackSize; + Context.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry; + Context.BlockListAddr = BlockListAddr; + Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64; + Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64; + + // + // Prepare data for return back + // + ReturnContext.ReturnCs = 0x10; + ReturnContext.ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction; + // + // Will save the return status of processing capsule + // + ReturnContext.ReturnStatus = 0; + + // + // Save original GDT + // + AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr); + + Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext); + + if (!EFI_ERROR (Status)) { + *MemoryBase = (VOID *) (UINTN) MemoryBase64; + *MemorySize = (UINTN) MemorySize64; + } + + return Status; + +} /** Checks for the presence of capsule descriptors. Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2... + and save to DescriptorBuffer. - @param BlockList Pointer to the capsule descriptors + @param DescriptorBuffer 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 + IN EFI_PHYSICAL_ADDRESS *DescriptorBuffer ) { EFI_STATUS Status; @@ -157,14 +441,8 @@ GetCapsuleDescriptors ( 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; @@ -204,20 +482,9 @@ GetCapsuleDescriptors ( // pointer is null. In that case, return success since we know that the // variable is set. // - if (BlockList == NULL) { + if (DescriptorBuffer == 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 ( @@ -237,7 +504,7 @@ GetCapsuleDescriptors ( // Flag = FALSE; for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) { - if (mBufferAddress[TempIndex] == CapsuleDataPtr64) { + if (DescriptorBuffer[TempIndex] == CapsuleDataPtr64) { Flag = TRUE; break; } @@ -246,500 +513,122 @@ GetCapsuleDescriptors ( 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; + DescriptorBuffer[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 + Locates the coalesce image entry point, and detects its machine type. - @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; - } + @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output. + @param CoalesceImageMachineType Pointer to machine type of coalesce image. - return TRUE; -} + @retval EFI_SUCCESS Coalesce image successfully located. + @retval Others Failed to locate the coalesce image. -/** - 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 +EFI_STATUS +FindCapsuleCoalesceImage ( + OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint, + OUT UINT16 *CoalesceImageMachineType ) { - 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++; - } + EFI_STATUS Status; + UINTN Instance; + EFI_PEI_LOAD_FILE_PPI *LoadFile; + EFI_PEI_FV_HANDLE VolumeHandle; + EFI_PEI_FILE_HANDLE FileHandle; + EFI_PHYSICAL_ADDRESS CoalesceImageAddress; + UINT64 CoalesceImageSize; + UINT32 AuthenticationState; - 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; - } + Instance = 0; - Failed = TRUE; + while (TRUE) { + Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle); + if (EFI_ERROR (Status)) { + return Status; } - // - // 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; - } + Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle); + if (!EFI_ERROR (Status)) { + Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile); + ASSERT_EFI_ERROR (Status); + + Status = LoadFile->LoadFile ( + LoadFile, + FileHandle, + &CoalesceImageAddress, + &CoalesceImageSize, + CoalesceImageEntryPoint, + &AuthenticationState + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleRelocate image ffs %r!\n", Status)); + return Status; } - CurrDesc++; - } - // - // Normal continuation -- jump to next block descriptor list - // - if (!Failed) { - CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) CurrDesc->Union.ContinuationPointer; + *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress); + break; + } else { + continue; } } - return MemBase; + + return Status; } /** - 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. + Gets the reserved long mode buffer. - 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 LongModeBuffer Pointer to the long mode buffer for output. - @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 EFI_SUCCESS Long mode buffer successfully retrieved. + @retval Others Variable storing long mode buffer not found. - @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_STATUS +GetLongModeContext ( + OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer ) { - 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++; - } + EFI_STATUS Status; + UINTN Size; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; - 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; - } + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **) &PPIVariableServices + ); + ASSERT_EFI_ERROR (Status); + + Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER); + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + EFI_CAPSULE_LONG_MODE_BUFFER_NAME, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + LongModeBuffer + ); + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status)); } - // - // Null terminate - // - CurrBlockDescHead->Union.ContinuationPointer = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL; - CurrBlockDescHead->Length = 0; - return NewBlockList; + return Status; } /** 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. @@ -768,54 +657,26 @@ CapsuleCoalesce ( 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; + UINTN Index; + UINTN Size; + UINTN VariableCount; + CHAR16 CapsuleVarName[30]; + CHAR16 *TempVarName; + EFI_PHYSICAL_ADDRESS CapsuleDataPtr64; + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + EFI_PHYSICAL_ADDRESS *VariableArrayAddress; +#ifdef MDE_CPU_IA32 + UINT16 CoalesceImageMachineType; + EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint; + COALESCE_ENTRY CoalesceEntry; + EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer; +#endif + + Index = 0; + VariableCount = 0; + CapsuleVarName[0] = 0; // // Someone should have already ascertained the boot mode. If it's not @@ -823,7 +684,9 @@ CapsuleCoalesce ( // Status = PeiServicesGetBootMode (&BootMode); if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) { - return EFI_NOT_FOUND; + DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update path.\n")); + Status = EFI_NOT_FOUND; + goto Done; } // @@ -837,7 +700,7 @@ CapsuleCoalesce ( (VOID **) &PPIVariableServices ); if (EFI_ERROR (Status)) { - return Status; + goto Done; } Size = sizeof (CapsuleDataPtr64); StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME); @@ -858,264 +721,94 @@ CapsuleCoalesce ( // // There is no capsule variables, quit // - DEBUG ((EFI_D_ERROR,"Capsule variable Index = %d\n", Index)); + DEBUG ((EFI_D_INFO,"Capsule variable Index = %d\n", Index)); break; } VariableCount++; Index++; } - DEBUG ((EFI_D_ERROR,"Capsule variable count = %d\n", VariableCount)); + DEBUG ((EFI_D_INFO,"Capsule variable count = %d\n", VariableCount)); + // + // The last entry is the end flag. + // Status = PeiServicesAllocatePool ( - VariableCount * sizeof (EFI_PHYSICAL_ADDRESS), - (VOID **)&mBufferAddress + (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS), + (VOID **)&VariableArrayAddress ); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status)); - return Status; + goto Done; } + ZeroMem (VariableArrayAddress, (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS)); + // // Find out if we actually have a capsule. + // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment. // - Status = GetCapsuleDescriptors (&BlockList); + Status = GetCapsuleDescriptors (VariableArrayAddress); 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; + DEBUG ((EFI_D_ERROR, "Fail to find capsule variables.\n")); + goto Done; } - 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) { +#ifdef MDE_CPU_IA32 + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { // - // Not enough room to relocate the descriptors + // Switch to 64-bit mode to process capsule data when: + // 1. When DXE phase is 64-bit + // 2. When the buffer for 64-bit transition exists + // 3. When Capsule X64 image is built in BIOS image + // In 64-bit mode, we can process capsule data above 4GB. // - 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++; + CoalesceImageEntryPoint = 0; + Status = GetLongModeContext (&LongModeBuffer); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Fail to find the variables for long mode context!\n")); + Status = EFI_NOT_FOUND; + goto Done; } - // - // 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; + + Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType); + if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) { + DEBUG ((EFI_D_ERROR, "Fail to find CapsuleX64 module in FV!\n")); + Status = EFI_NOT_FOUND; + goto Done; } + ASSERT (CoalesceImageEntryPoint != 0); + CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint; + Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryBase, MemorySize); + } else { // - //Walk through the block descriptor list. + // Capsule is processed in IA32 mode. // - CurrentBlockDesc++; + Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize); } +#else // - // We return the base of memory we want reserved, and the size. - // The memory peim should handle it appropriately from there. + // Process capsule directly. // - *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); + Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize); +#endif + + DEBUG ((EFI_D_INFO, "Capsule Coalesce Status = %r!\n", Status)); - PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase; - PrivateDataPtr->CapsuleSize = (UINTN)CapsuleImageSize; + if (Status == EFI_BUFFER_TOO_SMALL) { + DEBUG ((EFI_D_ERROR, "There is not enough memory to process capsule!\n")); + } + + if (Status == EFI_NOT_FOUND) { + DEBUG ((EFI_D_ERROR, "Fail to parse capsule descriptor in memory!\n")); + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR) + ); + } +Done: return Status; } @@ -1168,7 +861,10 @@ CapsuleTestPattern ( // is, then test it now. // TestPtr = (UINT32 *) CapsuleBase; - if (*TestPtr == CAPSULE_TEST_SIGNATURE) { + // + // 0x54534554 "TEST" + // + if (*TestPtr == 0x54534554) { RetValue = TRUE; DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n")); TestSize = TestPtr[1] / sizeof (UINT32); diff --git a/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c b/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c new file mode 100644 index 0000000000..c64fe1017c --- /dev/null +++ b/MdeModulePkg/Universal/CapsulePei/X64/X64Entry.c @@ -0,0 +1,66 @@ +/** @file + The X64 entrypoint is used to process capsule in long mode. + +Copyright (c) 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 +#include +#include "CommonHeader.h" + +/** + The X64 entrypoint is used to process capsule in long mode then + return to 32-bit protected mode. + + @param EntrypointContext Pointer to the context of long mode. + @param ReturnContext Pointer to the context of 32-bit protected mode. + + @retval This function should never return actually. + +**/ +EFI_STATUS +EFIAPI +_ModuleEntryPoint ( + SWITCH_32_TO_64_CONTEXT *EntrypointContext, + SWITCH_64_TO_32_CONTEXT *ReturnContext +) +{ + EFI_STATUS Status; + + // + // Call CapsuleDataCoalesce to process capsule. + // + Status = CapsuleDataCoalesce ( + NULL, + (EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr, + (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr, + (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr + ); + + ReturnContext->ReturnStatus = Status; + + // + // Finish to coalesce capsule, and return to 32-bit mode. + // + AsmDisablePaging64 ( + ReturnContext->ReturnCs, + (UINT32) ReturnContext->ReturnEntryPoint, + (UINT32) (UINTN) EntrypointContext, + (UINT32) (UINTN) ReturnContext, + (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength) + ); + + // + // Should never be here. + // + ASSERT (FALSE); + return EFI_SUCCESS; +} \ No newline at end of file -- cgit v1.2.3