From ab7017fe2bf2c1944090d347e7853e087d8641e8 Mon Sep 17 00:00:00 2001 From: li-elvin Date: Fri, 2 Sep 2011 11:34:35 +0000 Subject: [PATCH] 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/Include/Guid/CapsuleVendor.h | 14 +- MdeModulePkg/MdeModulePkg.dec | 8 + MdeModulePkg/Universal/CapsulePei/Capsule.h | 72 +- .../Universal/CapsulePei/CapsulePei.inf | 11 +- .../Universal/CapsulePei/CapsuleX64.inf | 48 + .../CapsulePei/Common/CapsuleCoalesce.c | 1104 +++++++++++++ .../CapsulePei/Common/CommonHeader.h | 84 + .../Universal/CapsulePei/UefiCapsule.c | 1360 +++++++---------- .../Universal/CapsulePei/X64/X64Entry.c | 66 + .../CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf | 25 + .../CapsuleRuntimeDxe/CapsuleService.c | 20 +- .../CapsuleRuntimeDxe/SaveLongModeContext.c | 27 + .../X64/SaveLongModeContext.c | 224 +++ 13 files changed, 2215 insertions(+), 848 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 create mode 100644 MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c create mode 100644 MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c diff --git a/MdeModulePkg/Include/Guid/CapsuleVendor.h b/MdeModulePkg/Include/Guid/CapsuleVendor.h index 0a1ebd94e6..15ca1055b6 100644 --- a/MdeModulePkg/Include/Guid/CapsuleVendor.h +++ b/MdeModulePkg/Include/Guid/CapsuleVendor.h @@ -9,7 +9,7 @@ @par Note: EDKII implementation of capsule updating has discarded this capsule GUID HOB data structure and used one UEFI Capsule HOB (defined in PI Specification 1.2) instead. -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 of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -48,6 +48,18 @@ typedef struct { UINT32 Length; ///< Length of capsule data. } CAPSULE_HOB_INFO; +// +// The variable describes the long mode buffer used by IA32 Capsule PEIM +// to call X64 CapsuleCoalesce code to handle >4GB capsule blocks. +// +#define EFI_CAPSULE_LONG_MODE_BUFFER_NAME L"CapsuleLongModeBuffer" + +typedef struct { + EFI_PHYSICAL_ADDRESS PageTableAddress; + EFI_PHYSICAL_ADDRESS StackBaseAddress; + UINT64 StackSize; +} EFI_CAPSULE_LONG_MODE_BUFFER; + extern EFI_GUID gEfiCapsuleVendorGuid; #endif // #ifndef _EFI_CAPSULE_VENDOR_GUID_H_ diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index ac345655ca..55626812f2 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -476,6 +476,9 @@ ## FFS filename to find the ACPI tables gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile|{ 0x25, 0x4e, 0x37, 0x7e, 0x01, 0x8e, 0xee, 0x4f, 0x87, 0xf2, 0x39, 0xc, 0x23, 0xc6, 0x6, 0xcd }|VOID*|0x30000016 + ## FFS filename to find the capsule coalesce image. + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleCoalesceFile|{ 0xA6, 0xE4, 0xFD, 0xF7, 0x4C, 0x29, 0x3c, 0x49, 0xB5, 0x0F, 0x97, 0x34, 0x55, 0x3B, 0xB7, 0x57 }|VOID*|0x30000017 + ## Single root I/O virtualization virtual function memory BAR alignment # BITN set indicates 2 of n+12 power # BIT0 set indicates 4KB alignment @@ -729,6 +732,11 @@ # script node created in runtime phase. gEfiMdeModulePkgTokenSpaceGuid.PcdS3BootScriptRuntimeTableReservePageNumber|0x2|UINT16|0x0001005C + ## The PCD is used to specify the stack size when capsule IA32 PEI transfers to long mode in PEI phase. + # The default size 32K. When changing the value of this PCD, the platform developer should + # make sure the memory size is large enough to meet capsule PEI requiremnt in capsule update path. + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsulePeiLongModeStackSize|0x8000|UINT32|0x0001005C + [PcdsPatchableInModule] ## Specify memory size with page number for PEI code when # the feature of Loading Module at Fixed Address is enabled 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 diff --git a/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf b/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf index 43388f55fc..e52b6a5f28 100644 --- a/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf +++ b/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf @@ -33,6 +33,12 @@ [Sources] CapsuleService.c +[Sources.Ia32, Sources.IPF, Sources.EBC, Sources.ARM] + SaveLongModeContext.c + +[Sources.X64] + X64/SaveLongModeContext.c + [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec @@ -47,19 +53,38 @@ UefiRuntimeLib BaseLib PrintLib + +[LibraryClasses.X64] + LockBoxLib + UefiLib + BaseMemoryLib + HobLib [Guids] gEfiCapsuleVendorGuid ## SOMETIMES_PRODUCED (Process across reset capsule image) ## Variable:L"CapsuleUpdateData" for capsule updated data +[Guids.X64] + gEfiAcpiVariableGuid # ALWAYS_CONSUMED + gEfiAcpiS3ContextGuid # ALWAYS_CONSUMED + [Protocols] gEfiCapsuleArchProtocolGuid ## PRODUCED +[Protocols.X64] + gEfiDxeSmmReadyToLockProtocolGuid # ALWAYS_CONSUMED + [FeaturePcd] gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset +[FeaturePcd.X64] + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode + [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizeNonPopulateCapsule gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizePopulateCapsule || gEfiMdeModulePkgTokenSpaceGuid.PcdSupportUpdateCapsuleReset ## Populate Image requires reset support. +[Pcd.X64] + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsulePeiLongModeStackSize + [Depex] gEfiVariableWriteArchProtocolGuid ## Depends on variable write functionality to produce capsule data variable diff --git a/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c b/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c index 76b2a2a8fc..17f167c978 100644 --- a/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c +++ b/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c @@ -4,7 +4,7 @@ It installs the Capsule Architectural Protocol defined in PI1.0a to signify the capsule runtime services are ready. -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 of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -40,6 +40,15 @@ EFI_HANDLE mNewHandle = NULL; // UINTN mTimes = 0; +/** + Create the variable to save the base address of page table and stack + for transferring into long mode in IA32 PEI. +**/ +VOID +SaveLongModeContext ( + VOID + ); + /** Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended consumption, the firmware may process the capsule immediately. If the payload should persist @@ -333,6 +342,15 @@ CapsuleServiceInitialize ( { EFI_STATUS Status; + // + // When PEI phase is IA32, DXE phase is X64, it is possible that capsule data are + // put above 4GB, so capsule PEI will transfer to long mode to get capsule data. + // The page table and stack is used to transfer processor mode from IA32 to long mode. + // Create the base address of page table and stack, and save them into variable. + // This is not needed when capsule with reset type is not supported. + // + SaveLongModeContext (); + // // Install capsule runtime services into UEFI runtime service tables. // diff --git a/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c b/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c new file mode 100644 index 0000000000..72cea7956b --- /dev/null +++ b/MdeModulePkg/Universal/CapsuleRuntimeDxe/SaveLongModeContext.c @@ -0,0 +1,27 @@ +/** @file + Create the NULL function to pass build in IA32/IPF/ARM/EBC. + +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 + +/** + Only when PEI is IA32 and DXE is X64, we need transfer to long mode in PEI + in order to process capsule data above 4GB. So create a NULL function here for + other cases. +**/ +VOID +SaveLongModeContext ( + VOID + ) +{ +} diff --git a/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c b/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c new file mode 100644 index 0000000000..0cfc352ac0 --- /dev/null +++ b/MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c @@ -0,0 +1,224 @@ +/** @file + Create the variable to save the base address of page table and stack + for transferring into long mode in IA32 capsule PEI. + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Allocate EfiACPIMemoryNVS below 4G memory address. + + This function allocates EfiACPIMemoryNVS below 4G memory address. + + @param Size Size of memory to allocate. + + @return Allocated address for output. + +**/ +VOID* +AllocateAcpiNvsMemoryBelow4G ( + IN UINTN Size + ) +{ + UINTN Pages; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + VOID* Buffer; + + Pages = EFI_SIZE_TO_PAGES (Size); + Address = 0xffffffff; + + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + Pages, + &Address + ); + ASSERT_EFI_ERROR (Status); + + Buffer = (VOID *) (UINTN) Address; + ZeroMem (Buffer, Size); + + return Buffer; +} + +/** + DxeSmmReadyToLock Protocol notification event handler. + We reuse S3 ACPI NVS reserved memory to do capsule process + after reset. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +DxeSmmReadyToLockNotification ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *DxeSmmReadyToLock; + UINTN VarSize; + EFI_PHYSICAL_ADDRESS TempAcpiS3Context; + ACPI_S3_CONTEXT *AcpiS3Context; + EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer; + UINTN TotalPagesNum; + UINT8 PhysicalAddressBits; + VOID *Hob; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + BOOLEAN LockBoxFound; + + Status = gBS->LocateProtocol ( + &gEfiDxeSmmReadyToLockProtocolGuid, + NULL, + &DxeSmmReadyToLock + ); + if (EFI_ERROR (Status)) { + return ; + } + + // + // Get the ACPI NVS pages reserved by AcpiS3Save + // + LockBoxFound = FALSE; + VarSize = sizeof (EFI_PHYSICAL_ADDRESS); + Status = RestoreLockBox ( + &gEfiAcpiVariableGuid, + &TempAcpiS3Context, + &VarSize + ); + if (!EFI_ERROR (Status)) { + AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context; + ASSERT (AcpiS3Context != NULL); + + Status = RestoreLockBox ( + &gEfiAcpiS3ContextGuid, + NULL, + NULL + ); + if (!EFI_ERROR (Status)) { + LongModeBuffer.PageTableAddress = AcpiS3Context->S3NvsPageTableAddress; + LongModeBuffer.StackBaseAddress = AcpiS3Context->BootScriptStackBase; + LongModeBuffer.StackSize = AcpiS3Context->BootScriptStackSize; + LockBoxFound = TRUE; + } + } + + if (!LockBoxFound) { + // + // Page table base address and stack base address can not be found in lock box, + // allocate both here. + // + + // + // 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 page table size and allocate memory for it. + // + if (PhysicalAddressBits <= 39 ) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30); + } else { + NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39); + NumberOfPdpEntriesNeeded = 512; + } + + TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1; + LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum)); + ASSERT (LongModeBuffer.PageTableAddress != 0); + + // + // Allocate stack + // + LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize); + LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize)); + ASSERT (LongModeBuffer.StackBaseAddress != 0); + } + + Status = gRT->SetVariable ( + EFI_CAPSULE_LONG_MODE_BUFFER_NAME, + &gEfiCapsuleVendorGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (EFI_CAPSULE_LONG_MODE_BUFFER), + &LongModeBuffer + ); + ASSERT_EFI_ERROR (Status); + + // + // Close event, so it will not be invoked again. + // + gBS->CloseEvent (Event); + + return ; +} + +/** + Create the variable to save the base address of page table and stack + for transferring into long mode in IA32 capsule PEI. +**/ +VOID +SaveLongModeContext ( + VOID + ) +{ + VOID *Registration; + + if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) { + // + // Register event to get ACPI NVS pages reserved from lock box, these pages will be used by + // Capsule IA32 PEI to transfer to long mode to access capsule above 4GB. + // + EfiCreateProtocolNotifyEvent ( + &gEfiDxeSmmReadyToLockProtocolGuid, + TPL_CALLBACK, + DxeSmmReadyToLockNotification, + NULL, + &Registration + ); + } +} -- 2.39.2